packages feed

template-toolkit 0.1.0.1 → 0.1.1.0

raw patch · 12 files changed

+2108/−2670 lines, 12 filesdep +binarydep +hashtablesdep +timedep ~aesondep ~basedep ~bytestringsetup-changedPVP ok

version bump matches the API change (PVP)

Dependencies added: binary, hashtables, time

Dependency ranges changed: aeson, base, bytestring, containers, directory, mtl, parsec, pcre-utils, regex-pcre-builtin, scientific, text, unordered-containers, uri-encode

API changes (from Hackage documentation)

+ Text.TemplateToolkitAST: Add :: BinOp
+ Text.TemplateToolkitAST: And :: BinOp
+ Text.TemplateToolkitAST: Con :: BinOp
+ Text.TemplateToolkitAST: Div :: BinOp
+ Text.TemplateToolkitAST: EAssign :: Var -> Expr -> Expr
+ Text.TemplateToolkitAST: EBinOp :: BinOp -> Expr -> Expr -> Expr
+ Text.TemplateToolkitAST: ETerOp :: Expr -> Expr -> Expr -> Expr
+ Text.TemplateToolkitAST: EUnOp :: UnOp -> Expr -> Expr
+ Text.TemplateToolkitAST: EVal :: Val -> Expr
+ Text.TemplateToolkitAST: EVar :: Var -> Expr
+ Text.TemplateToolkitAST: Else :: Stmt -> Else
+ Text.TemplateToolkitAST: Elsif :: Expr -> Stmt -> Maybe Else -> Else
+ Text.TemplateToolkitAST: Eq :: BinOp
+ Text.TemplateToolkitAST: Ge :: BinOp
+ Text.TemplateToolkitAST: Gt :: BinOp
+ Text.TemplateToolkitAST: IString :: Text -> IString
+ Text.TemplateToolkitAST: IVar :: Var -> IString
+ Text.TemplateToolkitAST: Le :: BinOp
+ Text.TemplateToolkitAST: Lt :: BinOp
+ Text.TemplateToolkitAST: Mod :: BinOp
+ Text.TemplateToolkitAST: Mul :: BinOp
+ Text.TemplateToolkitAST: Ne :: BinOp
+ Text.TemplateToolkitAST: Neg :: UnOp
+ Text.TemplateToolkitAST: Not :: UnOp
+ Text.TemplateToolkitAST: Or :: BinOp
+ Text.TemplateToolkitAST: Pos :: UnOp
+ Text.TemplateToolkitAST: SAssign :: Var -> Expr -> StmtId -> LineN -> Stmt
+ Text.TemplateToolkitAST: SBlock :: String -> Stmt -> StmtId -> LineN -> Stmt
+ Text.TemplateToolkitAST: SComment :: StmtId -> LineN -> Stmt
+ Text.TemplateToolkitAST: SExpr :: Expr -> StmtId -> LineN -> Stmt
+ Text.TemplateToolkitAST: SFilter :: String -> [Expr] -> Stmt -> StmtId -> LineN -> Stmt
+ Text.TemplateToolkitAST: SForeach :: Var -> Expr -> Stmt -> StmtId -> LineN -> Stmt
+ Text.TemplateToolkitAST: SIf :: Expr -> Stmt -> Maybe Else -> StmtId -> LineN -> Stmt
+ Text.TemplateToolkitAST: SLast :: StmtId -> LineN -> Stmt
+ Text.TemplateToolkitAST: SNext :: StmtId -> LineN -> Stmt
+ Text.TemplateToolkitAST: SProcess :: IString -> [Stmt] -> StmtId -> LineN -> Stmt
+ Text.TemplateToolkitAST: SText :: Text -> StmtId -> LineN -> Stmt
+ Text.TemplateToolkitAST: SWhile :: Expr -> Stmt -> StmtId -> LineN -> Stmt
+ Text.TemplateToolkitAST: SWrapper :: IString -> [Stmt] -> Stmt -> StmtId -> LineN -> Stmt
+ Text.TemplateToolkitAST: Seq :: [Stmt] -> StmtId -> LineN -> Stmt
+ Text.TemplateToolkitAST: Sub :: BinOp
+ Text.TemplateToolkitAST: Undef :: Val
+ Text.TemplateToolkitAST: VArray :: [Expr] -> Val
+ Text.TemplateToolkitAST: VArrayRange :: Expr -> Expr -> Val
+ Text.TemplateToolkitAST: VArrayV :: Array -> Val
+ Text.TemplateToolkitAST: VCode :: Stmt -> Val
+ Text.TemplateToolkitAST: VFloat :: Double -> Val
+ Text.TemplateToolkitAST: VHash :: [(String, Expr)] -> Val
+ Text.TemplateToolkitAST: VHashV :: Hash -> Val
+ Text.TemplateToolkitAST: VIString :: [IString] -> Val
+ Text.TemplateToolkitAST: VInt :: Integer -> Val
+ Text.TemplateToolkitAST: VRef :: String -> Val
+ Text.TemplateToolkitAST: VString :: Text -> Val
+ Text.TemplateToolkitAST: Var :: [VarNode] -> Var
+ Text.TemplateToolkitAST: VarIndex :: Int -> VarNode
+ Text.TemplateToolkitAST: VarKey :: String -> VarNode
+ Text.TemplateToolkitAST: VarMethod :: String -> [Expr] -> VarNode
+ Text.TemplateToolkitAST: VarRef :: Var -> VarNode
+ Text.TemplateToolkitAST: [lineN] :: Stmt -> LineN
+ Text.TemplateToolkitAST: [sAssignExpr] :: Stmt -> Expr
+ Text.TemplateToolkitAST: [sAssignVar] :: Stmt -> Var
+ Text.TemplateToolkitAST: [sBlockName] :: Stmt -> String
+ Text.TemplateToolkitAST: [sBody] :: Stmt -> Stmt
+ Text.TemplateToolkitAST: [sCond] :: Stmt -> Expr
+ Text.TemplateToolkitAST: [sElse] :: Stmt -> Maybe Else
+ Text.TemplateToolkitAST: [sExpr] :: Stmt -> Expr
+ Text.TemplateToolkitAST: [sFilterName] :: Stmt -> String
+ Text.TemplateToolkitAST: [sFilterParams] :: Stmt -> [Expr]
+ Text.TemplateToolkitAST: [sForeachArr] :: Stmt -> Expr
+ Text.TemplateToolkitAST: [sForeachVar] :: Stmt -> Var
+ Text.TemplateToolkitAST: [sId] :: Stmt -> StmtId
+ Text.TemplateToolkitAST: [sProcessAssigns] :: Stmt -> [Stmt]
+ Text.TemplateToolkitAST: [sProcessName] :: Stmt -> IString
+ Text.TemplateToolkitAST: [sSeq] :: Stmt -> [Stmt]
+ Text.TemplateToolkitAST: [sText] :: Stmt -> Text
+ Text.TemplateToolkitAST: [sWrapperAssigns] :: Stmt -> [Stmt]
+ Text.TemplateToolkitAST: [sWrapperName] :: Stmt -> IString
+ Text.TemplateToolkitAST: data BinOp
+ Text.TemplateToolkitAST: data Else
+ Text.TemplateToolkitAST: data Expr
+ Text.TemplateToolkitAST: data IString
+ Text.TemplateToolkitAST: data Stmt
+ Text.TemplateToolkitAST: data UnOp
+ Text.TemplateToolkitAST: data Val
+ Text.TemplateToolkitAST: data Var
+ Text.TemplateToolkitAST: data VarNode
+ Text.TemplateToolkitAST: instance Data.Binary.Class.Binary Text.TemplateToolkitAST.BinOp
+ Text.TemplateToolkitAST: instance Data.Binary.Class.Binary Text.TemplateToolkitAST.Else
+ Text.TemplateToolkitAST: instance Data.Binary.Class.Binary Text.TemplateToolkitAST.Expr
+ Text.TemplateToolkitAST: instance Data.Binary.Class.Binary Text.TemplateToolkitAST.IString
+ Text.TemplateToolkitAST: instance Data.Binary.Class.Binary Text.TemplateToolkitAST.Stmt
+ Text.TemplateToolkitAST: instance Data.Binary.Class.Binary Text.TemplateToolkitAST.UnOp
+ Text.TemplateToolkitAST: instance Data.Binary.Class.Binary Text.TemplateToolkitAST.Val
+ Text.TemplateToolkitAST: instance Data.Binary.Class.Binary Text.TemplateToolkitAST.Var
+ Text.TemplateToolkitAST: instance Data.Binary.Class.Binary Text.TemplateToolkitAST.VarNode
+ Text.TemplateToolkitAST: instance GHC.Generics.Generic Text.TemplateToolkitAST.BinOp
+ Text.TemplateToolkitAST: instance GHC.Generics.Generic Text.TemplateToolkitAST.Else
+ Text.TemplateToolkitAST: instance GHC.Generics.Generic Text.TemplateToolkitAST.Expr
+ Text.TemplateToolkitAST: instance GHC.Generics.Generic Text.TemplateToolkitAST.IString
+ Text.TemplateToolkitAST: instance GHC.Generics.Generic Text.TemplateToolkitAST.Stmt
+ Text.TemplateToolkitAST: instance GHC.Generics.Generic Text.TemplateToolkitAST.UnOp
+ Text.TemplateToolkitAST: instance GHC.Generics.Generic Text.TemplateToolkitAST.Val
+ Text.TemplateToolkitAST: instance GHC.Generics.Generic Text.TemplateToolkitAST.Var
+ Text.TemplateToolkitAST: instance GHC.Generics.Generic Text.TemplateToolkitAST.VarNode
+ Text.TemplateToolkitAST: instance GHC.Show.Show Text.TemplateToolkitAST.BinOp
+ Text.TemplateToolkitAST: instance GHC.Show.Show Text.TemplateToolkitAST.Else
+ Text.TemplateToolkitAST: instance GHC.Show.Show Text.TemplateToolkitAST.Expr
+ Text.TemplateToolkitAST: instance GHC.Show.Show Text.TemplateToolkitAST.IString
+ Text.TemplateToolkitAST: instance GHC.Show.Show Text.TemplateToolkitAST.Stmt
+ Text.TemplateToolkitAST: instance GHC.Show.Show Text.TemplateToolkitAST.UnOp
+ Text.TemplateToolkitAST: instance GHC.Show.Show Text.TemplateToolkitAST.Val
+ Text.TemplateToolkitAST: instance GHC.Show.Show Text.TemplateToolkitAST.Var
+ Text.TemplateToolkitAST: instance GHC.Show.Show Text.TemplateToolkitAST.VarNode
+ Text.TemplateToolkitAST: parseTemplate :: TName -> Text -> Either ParseError Stmt
+ Text.TemplateToolkitAST: parseTemplateWithStmtId :: Text -> TName -> StmtId -> Either ParseError Stmt
+ Text.TemplateToolkitAST: type Array = Seq Val
+ Text.TemplateToolkitAST: type StmtId = (Int, Int, Int)
+ Text.TemplateToolkitAST: type TName = String

Files

+ ChangeLog.md view
@@ -0,0 +1,11 @@+# Changelog for template-toolkit++## Unreleased changes+* Passing custom functions to templates+* Plugins: DBI, Dumper, etc.++## [0.1.1.0] - 2018-12-03+Templates caching feature added++## [0.1.0.1] - 2017-10-19+Initial release
LICENSE view
@@ -1,674 +1,15 @@-              GNU GENERAL PUBLIC LICENSE
-                Version 3, 29 June 2007
-
- Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
- Everyone is permitted to copy and distribute verbatim copies
- of this license document, but changing it is not allowed.
-
-                     Preamble
-
-  The GNU General Public License is a free, copyleft license for
-software and other kinds of works.
-
-  The licenses for most software and other practical works are designed
-to take away your freedom to share and change the works.  By contrast,
-the GNU General Public License is intended to guarantee your freedom to
-share and change all versions of a program--to make sure it remains free
-software for all its users.  We, the Free Software Foundation, use the
-GNU General Public License for most of our software; it applies also to
-any other work released this way by its authors.  You can apply it to
-your programs, too.
-
-  When we speak of free software, we are referring to freedom, not
-price.  Our General Public Licenses are designed to make sure that you
-have the freedom to distribute copies of free software (and charge for
-them if you wish), that you receive source code or can get it if you
-want it, that you can change the software or use pieces of it in new
-free programs, and that you know you can do these things.
-
-  To protect your rights, we need to prevent others from denying you
-these rights or asking you to surrender the rights.  Therefore, you have
-certain responsibilities if you distribute copies of the software, or if
-you modify it: responsibilities to respect the freedom of others.
-
-  For example, if you distribute copies of such a program, whether
-gratis or for a fee, you must pass on to the recipients the same
-freedoms that you received.  You must make sure that they, too, receive
-or can get the source code.  And you must show them these terms so they
-know their rights.
-
-  Developers that use the GNU GPL protect your rights with two steps:
-(1) assert copyright on the software, and (2) offer you this License
-giving you legal permission to copy, distribute and/or modify it.
-
-  For the developers' and authors' protection, the GPL clearly explains
-that there is no warranty for this free software.  For both users' and
-authors' sake, the GPL requires that modified versions be marked as
-changed, so that their problems will not be attributed erroneously to
-authors of previous versions.
-
-  Some devices are designed to deny users access to install or run
-modified versions of the software inside them, although the manufacturer
-can do so.  This is fundamentally incompatible with the aim of
-protecting users' freedom to change the software.  The systematic
-pattern of such abuse occurs in the area of products for individuals to
-use, which is precisely where it is most unacceptable.  Therefore, we
-have designed this version of the GPL to prohibit the practice for those
-products.  If such problems arise substantially in other domains, we
-stand ready to extend this provision to those domains in future versions
-of the GPL, as needed to protect the freedom of users.
-
-  Finally, every program is threatened constantly by software patents.
-States should not allow patents to restrict development and use of
-software on general-purpose computers, but in those that do, we wish to
-avoid the special danger that patents applied to a free program could
-make it effectively proprietary.  To prevent this, the GPL assures that
-patents cannot be used to render the program non-free.
-
-  The precise terms and conditions for copying, distribution and
-modification follow.
-
-                TERMS AND CONDITIONS
-
-  0. Definitions.
-
-  "This License" refers to version 3 of the GNU General Public License.
-
-  "Copyright" also means copyright-like laws that apply to other kinds of
-works, such as semiconductor masks.
-
-  "The Program" refers to any copyrightable work licensed under this
-License.  Each licensee is addressed as "you".  "Licensees" and
-"recipients" may be individuals or organizations.
-
-  To "modify" a work means to copy from or adapt all or part of the work
-in a fashion requiring copyright permission, other than the making of an
-exact copy.  The resulting work is called a "modified version" of the
-earlier work or a work "based on" the earlier work.
-
-  A "covered work" means either the unmodified Program or a work based
-on the Program.
-
-  To "propagate" a work means to do anything with it that, without
-permission, would make you directly or secondarily liable for
-infringement under applicable copyright law, except executing it on a
-computer or modifying a private copy.  Propagation includes copying,
-distribution (with or without modification), making available to the
-public, and in some countries other activities as well.
-
-  To "convey" a work means any kind of propagation that enables other
-parties to make or receive copies.  Mere interaction with a user through
-a computer network, with no transfer of a copy, is not conveying.
-
-  An interactive user interface displays "Appropriate Legal Notices"
-to the extent that it includes a convenient and prominently visible
-feature that (1) displays an appropriate copyright notice, and (2)
-tells the user that there is no warranty for the work (except to the
-extent that warranties are provided), that licensees may convey the
-work under this License, and how to view a copy of this License.  If
-the interface presents a list of user commands or options, such as a
-menu, a prominent item in the list meets this criterion.
-
-  1. Source Code.
-
-  The "source code" for a work means the preferred form of the work
-for making modifications to it.  "Object code" means any non-source
-form of a work.
-
-  A "Standard Interface" means an interface that either is an official
-standard defined by a recognized standards body, or, in the case of
-interfaces specified for a particular programming language, one that
-is widely used among developers working in that language.
-
-  The "System Libraries" of an executable work include anything, other
-than the work as a whole, that (a) is included in the normal form of
-packaging a Major Component, but which is not part of that Major
-Component, and (b) serves only to enable use of the work with that
-Major Component, or to implement a Standard Interface for which an
-implementation is available to the public in source code form.  A
-"Major Component", in this context, means a major essential component
-(kernel, window system, and so on) of the specific operating system
-(if any) on which the executable work runs, or a compiler used to
-produce the work, or an object code interpreter used to run it.
-
-  The "Corresponding Source" for a work in object code form means all
-the source code needed to generate, install, and (for an executable
-work) run the object code and to modify the work, including scripts to
-control those activities.  However, it does not include the work's
-System Libraries, or general-purpose tools or generally available free
-programs which are used unmodified in performing those activities but
-which are not part of the work.  For example, Corresponding Source
-includes interface definition files associated with source files for
-the work, and the source code for shared libraries and dynamically
-linked subprograms that the work is specifically designed to require,
-such as by intimate data communication or control flow between those
-subprograms and other parts of the work.
-
-  The Corresponding Source need not include anything that users
-can regenerate automatically from other parts of the Corresponding
-Source.
-
-  The Corresponding Source for a work in source code form is that
-same work.
-
-  2. Basic Permissions.
-
-  All rights granted under this License are granted for the term of
-copyright on the Program, and are irrevocable provided the stated
-conditions are met.  This License explicitly affirms your unlimited
-permission to run the unmodified Program.  The output from running a
-covered work is covered by this License only if the output, given its
-content, constitutes a covered work.  This License acknowledges your
-rights of fair use or other equivalent, as provided by copyright law.
-
-  You may make, run and propagate covered works that you do not
-convey, without conditions so long as your license otherwise remains
-in force.  You may convey covered works to others for the sole purpose
-of having them make modifications exclusively for you, or provide you
-with facilities for running those works, provided that you comply with
-the terms of this License in conveying all material for which you do
-not control copyright.  Those thus making or running the covered works
-for you must do so exclusively on your behalf, under your direction
-and control, on terms that prohibit them from making any copies of
-your copyrighted material outside their relationship with you.
-
-  Conveying under any other circumstances is permitted solely under
-the conditions stated below.  Sublicensing is not allowed; section 10
-makes it unnecessary.
-
-  3. Protecting Users' Legal Rights From Anti-Circumvention Law.
-
-  No covered work shall be deemed part of an effective technological
-measure under any applicable law fulfilling obligations under article
-11 of the WIPO copyright treaty adopted on 20 December 1996, or
-similar laws prohibiting or restricting circumvention of such
-measures.
-
-  When you convey a covered work, you waive any legal power to forbid
-circumvention of technological measures to the extent such circumvention
-is effected by exercising rights under this License with respect to
-the covered work, and you disclaim any intention to limit operation or
-modification of the work as a means of enforcing, against the work's
-users, your or third parties' legal rights to forbid circumvention of
-technological measures.
-
-  4. Conveying Verbatim Copies.
-
-  You may convey verbatim copies of the Program's source code as you
-receive it, in any medium, provided that you conspicuously and
-appropriately publish on each copy an appropriate copyright notice;
-keep intact all notices stating that this License and any
-non-permissive terms added in accord with section 7 apply to the code;
-keep intact all notices of the absence of any warranty; and give all
-recipients a copy of this License along with the Program.
-
-  You may charge any price or no price for each copy that you convey,
-and you may offer support or warranty protection for a fee.
-
-  5. Conveying Modified Source Versions.
-
-  You may convey a work based on the Program, or the modifications to
-produce it from the Program, in the form of source code under the
-terms of section 4, provided that you also meet all of these conditions:
-
-    a) The work must carry prominent notices stating that you modified
-    it, and giving a relevant date.
-
-    b) The work must carry prominent notices stating that it is
-    released under this License and any conditions added under section
-    7.  This requirement modifies the requirement in section 4 to
-    "keep intact all notices".
-
-    c) You must license the entire work, as a whole, under this
-    License to anyone who comes into possession of a copy.  This
-    License will therefore apply, along with any applicable section 7
-    additional terms, to the whole of the work, and all its parts,
-    regardless of how they are packaged.  This License gives no
-    permission to license the work in any other way, but it does not
-    invalidate such permission if you have separately received it.
-
-    d) If the work has interactive user interfaces, each must display
-    Appropriate Legal Notices; however, if the Program has interactive
-    interfaces that do not display Appropriate Legal Notices, your
-    work need not make them do so.
-
-  A compilation of a covered work with other separate and independent
-works, which are not by their nature extensions of the covered work,
-and which are not combined with it such as to form a larger program,
-in or on a volume of a storage or distribution medium, is called an
-"aggregate" if the compilation and its resulting copyright are not
-used to limit the access or legal rights of the compilation's users
-beyond what the individual works permit.  Inclusion of a covered work
-in an aggregate does not cause this License to apply to the other
-parts of the aggregate.
-
-  6. Conveying Non-Source Forms.
-
-  You may convey a covered work in object code form under the terms
-of sections 4 and 5, provided that you also convey the
-machine-readable Corresponding Source under the terms of this License,
-in one of these ways:
-
-    a) Convey the object code in, or embodied in, a physical product
-    (including a physical distribution medium), accompanied by the
-    Corresponding Source fixed on a durable physical medium
-    customarily used for software interchange.
-
-    b) Convey the object code in, or embodied in, a physical product
-    (including a physical distribution medium), accompanied by a
-    written offer, valid for at least three years and valid for as
-    long as you offer spare parts or customer support for that product
-    model, to give anyone who possesses the object code either (1) a
-    copy of the Corresponding Source for all the software in the
-    product that is covered by this License, on a durable physical
-    medium customarily used for software interchange, for a price no
-    more than your reasonable cost of physically performing this
-    conveying of source, or (2) access to copy the
-    Corresponding Source from a network server at no charge.
-
-    c) Convey individual copies of the object code with a copy of the
-    written offer to provide the Corresponding Source.  This
-    alternative is allowed only occasionally and noncommercially, and
-    only if you received the object code with such an offer, in accord
-    with subsection 6b.
-
-    d) Convey the object code by offering access from a designated
-    place (gratis or for a charge), and offer equivalent access to the
-    Corresponding Source in the same way through the same place at no
-    further charge.  You need not require recipients to copy the
-    Corresponding Source along with the object code.  If the place to
-    copy the object code is a network server, the Corresponding Source
-    may be on a different server (operated by you or a third party)
-    that supports equivalent copying facilities, provided you maintain
-    clear directions next to the object code saying where to find the
-    Corresponding Source.  Regardless of what server hosts the
-    Corresponding Source, you remain obligated to ensure that it is
-    available for as long as needed to satisfy these requirements.
-
-    e) Convey the object code using peer-to-peer transmission, provided
-    you inform other peers where the object code and Corresponding
-    Source of the work are being offered to the general public at no
-    charge under subsection 6d.
-
-  A separable portion of the object code, whose source code is excluded
-from the Corresponding Source as a System Library, need not be
-included in conveying the object code work.
-
-  A "User Product" is either (1) a "consumer product", which means any
-tangible personal property which is normally used for personal, family,
-or household purposes, or (2) anything designed or sold for incorporation
-into a dwelling.  In determining whether a product is a consumer product,
-doubtful cases shall be resolved in favor of coverage.  For a particular
-product received by a particular user, "normally used" refers to a
-typical or common use of that class of product, regardless of the status
-of the particular user or of the way in which the particular user
-actually uses, or expects or is expected to use, the product.  A product
-is a consumer product regardless of whether the product has substantial
-commercial, industrial or non-consumer uses, unless such uses represent
-the only significant mode of use of the product.
-
-  "Installation Information" for a User Product means any methods,
-procedures, authorization keys, or other information required to install
-and execute modified versions of a covered work in that User Product from
-a modified version of its Corresponding Source.  The information must
-suffice to ensure that the continued functioning of the modified object
-code is in no case prevented or interfered with solely because
-modification has been made.
-
-  If you convey an object code work under this section in, or with, or
-specifically for use in, a User Product, and the conveying occurs as
-part of a transaction in which the right of possession and use of the
-User Product is transferred to the recipient in perpetuity or for a
-fixed term (regardless of how the transaction is characterized), the
-Corresponding Source conveyed under this section must be accompanied
-by the Installation Information.  But this requirement does not apply
-if neither you nor any third party retains the ability to install
-modified object code on the User Product (for example, the work has
-been installed in ROM).
-
-  The requirement to provide Installation Information does not include a
-requirement to continue to provide support service, warranty, or updates
-for a work that has been modified or installed by the recipient, or for
-the User Product in which it has been modified or installed.  Access to a
-network may be denied when the modification itself materially and
-adversely affects the operation of the network or violates the rules and
-protocols for communication across the network.
-
-  Corresponding Source conveyed, and Installation Information provided,
-in accord with this section must be in a format that is publicly
-documented (and with an implementation available to the public in
-source code form), and must require no special password or key for
-unpacking, reading or copying.
-
-  7. Additional Terms.
-
-  "Additional permissions" are terms that supplement the terms of this
-License by making exceptions from one or more of its conditions.
-Additional permissions that are applicable to the entire Program shall
-be treated as though they were included in this License, to the extent
-that they are valid under applicable law.  If additional permissions
-apply only to part of the Program, that part may be used separately
-under those permissions, but the entire Program remains governed by
-this License without regard to the additional permissions.
-
-  When you convey a copy of a covered work, you may at your option
-remove any additional permissions from that copy, or from any part of
-it.  (Additional permissions may be written to require their own
-removal in certain cases when you modify the work.)  You may place
-additional permissions on material, added by you to a covered work,
-for which you have or can give appropriate copyright permission.
-
-  Notwithstanding any other provision of this License, for material you
-add to a covered work, you may (if authorized by the copyright holders of
-that material) supplement the terms of this License with terms:
-
-    a) Disclaiming warranty or limiting liability differently from the
-    terms of sections 15 and 16 of this License; or
-
-    b) Requiring preservation of specified reasonable legal notices or
-    author attributions in that material or in the Appropriate Legal
-    Notices displayed by works containing it; or
-
-    c) Prohibiting misrepresentation of the origin of that material, or
-    requiring that modified versions of such material be marked in
-    reasonable ways as different from the original version; or
-
-    d) Limiting the use for publicity purposes of names of licensors or
-    authors of the material; or
-
-    e) Declining to grant rights under trademark law for use of some
-    trade names, trademarks, or service marks; or
-
-    f) Requiring indemnification of licensors and authors of that
-    material by anyone who conveys the material (or modified versions of
-    it) with contractual assumptions of liability to the recipient, for
-    any liability that these contractual assumptions directly impose on
-    those licensors and authors.
-
-  All other non-permissive additional terms are considered "further
-restrictions" within the meaning of section 10.  If the Program as you
-received it, or any part of it, contains a notice stating that it is
-governed by this License along with a term that is a further
-restriction, you may remove that term.  If a license document contains
-a further restriction but permits relicensing or conveying under this
-License, you may add to a covered work material governed by the terms
-of that license document, provided that the further restriction does
-not survive such relicensing or conveying.
-
-  If you add terms to a covered work in accord with this section, you
-must place, in the relevant source files, a statement of the
-additional terms that apply to those files, or a notice indicating
-where to find the applicable terms.
-
-  Additional terms, permissive or non-permissive, may be stated in the
-form of a separately written license, or stated as exceptions;
-the above requirements apply either way.
-
-  8. Termination.
-
-  You may not propagate or modify a covered work except as expressly
-provided under this License.  Any attempt otherwise to propagate or
-modify it is void, and will automatically terminate your rights under
-this License (including any patent licenses granted under the third
-paragraph of section 11).
-
-  However, if you cease all violation of this License, then your
-license from a particular copyright holder is reinstated (a)
-provisionally, unless and until the copyright holder explicitly and
-finally terminates your license, and (b) permanently, if the copyright
-holder fails to notify you of the violation by some reasonable means
-prior to 60 days after the cessation.
-
-  Moreover, your license from a particular copyright holder is
-reinstated permanently if the copyright holder notifies you of the
-violation by some reasonable means, this is the first time you have
-received notice of violation of this License (for any work) from that
-copyright holder, and you cure the violation prior to 30 days after
-your receipt of the notice.
-
-  Termination of your rights under this section does not terminate the
-licenses of parties who have received copies or rights from you under
-this License.  If your rights have been terminated and not permanently
-reinstated, you do not qualify to receive new licenses for the same
-material under section 10.
-
-  9. Acceptance Not Required for Having Copies.
-
-  You are not required to accept this License in order to receive or
-run a copy of the Program.  Ancillary propagation of a covered work
-occurring solely as a consequence of using peer-to-peer transmission
-to receive a copy likewise does not require acceptance.  However,
-nothing other than this License grants you permission to propagate or
-modify any covered work.  These actions infringe copyright if you do
-not accept this License.  Therefore, by modifying or propagating a
-covered work, you indicate your acceptance of this License to do so.
-
-  10. Automatic Licensing of Downstream Recipients.
-
-  Each time you convey a covered work, the recipient automatically
-receives a license from the original licensors, to run, modify and
-propagate that work, subject to this License.  You are not responsible
-for enforcing compliance by third parties with this License.
-
-  An "entity transaction" is a transaction transferring control of an
-organization, or substantially all assets of one, or subdividing an
-organization, or merging organizations.  If propagation of a covered
-work results from an entity transaction, each party to that
-transaction who receives a copy of the work also receives whatever
-licenses to the work the party's predecessor in interest had or could
-give under the previous paragraph, plus a right to possession of the
-Corresponding Source of the work from the predecessor in interest, if
-the predecessor has it or can get it with reasonable efforts.
-
-  You may not impose any further restrictions on the exercise of the
-rights granted or affirmed under this License.  For example, you may
-not impose a license fee, royalty, or other charge for exercise of
-rights granted under this License, and you may not initiate litigation
-(including a cross-claim or counterclaim in a lawsuit) alleging that
-any patent claim is infringed by making, using, selling, offering for
-sale, or importing the Program or any portion of it.
-
-  11. Patents.
-
-  A "contributor" is a copyright holder who authorizes use under this
-License of the Program or a work on which the Program is based.  The
-work thus licensed is called the contributor's "contributor version".
-
-  A contributor's "essential patent claims" are all patent claims
-owned or controlled by the contributor, whether already acquired or
-hereafter acquired, that would be infringed by some manner, permitted
-by this License, of making, using, or selling its contributor version,
-but do not include claims that would be infringed only as a
-consequence of further modification of the contributor version.  For
-purposes of this definition, "control" includes the right to grant
-patent sublicenses in a manner consistent with the requirements of
-this License.
-
-  Each contributor grants you a non-exclusive, worldwide, royalty-free
-patent license under the contributor's essential patent claims, to
-make, use, sell, offer for sale, import and otherwise run, modify and
-propagate the contents of its contributor version.
-
-  In the following three paragraphs, a "patent license" is any express
-agreement or commitment, however denominated, not to enforce a patent
-(such as an express permission to practice a patent or covenant not to
-sue for patent infringement).  To "grant" such a patent license to a
-party means to make such an agreement or commitment not to enforce a
-patent against the party.
-
-  If you convey a covered work, knowingly relying on a patent license,
-and the Corresponding Source of the work is not available for anyone
-to copy, free of charge and under the terms of this License, through a
-publicly available network server or other readily accessible means,
-then you must either (1) cause the Corresponding Source to be so
-available, or (2) arrange to deprive yourself of the benefit of the
-patent license for this particular work, or (3) arrange, in a manner
-consistent with the requirements of this License, to extend the patent
-license to downstream recipients.  "Knowingly relying" means you have
-actual knowledge that, but for the patent license, your conveying the
-covered work in a country, or your recipient's use of the covered work
-in a country, would infringe one or more identifiable patents in that
-country that you have reason to believe are valid.
-
-  If, pursuant to or in connection with a single transaction or
-arrangement, you convey, or propagate by procuring conveyance of, a
-covered work, and grant a patent license to some of the parties
-receiving the covered work authorizing them to use, propagate, modify
-or convey a specific copy of the covered work, then the patent license
-you grant is automatically extended to all recipients of the covered
-work and works based on it.
-
-  A patent license is "discriminatory" if it does not include within
-the scope of its coverage, prohibits the exercise of, or is
-conditioned on the non-exercise of one or more of the rights that are
-specifically granted under this License.  You may not convey a covered
-work if you are a party to an arrangement with a third party that is
-in the business of distributing software, under which you make payment
-to the third party based on the extent of your activity of conveying
-the work, and under which the third party grants, to any of the
-parties who would receive the covered work from you, a discriminatory
-patent license (a) in connection with copies of the covered work
-conveyed by you (or copies made from those copies), or (b) primarily
-for and in connection with specific products or compilations that
-contain the covered work, unless you entered into that arrangement,
-or that patent license was granted, prior to 28 March 2007.
-
-  Nothing in this License shall be construed as excluding or limiting
-any implied license or other defenses to infringement that may
-otherwise be available to you under applicable patent law.
-
-  12. No Surrender of Others' Freedom.
-
-  If conditions are imposed on you (whether by court order, agreement or
-otherwise) that contradict the conditions of this License, they do not
-excuse you from the conditions of this License.  If you cannot convey a
-covered work so as to satisfy simultaneously your obligations under this
-License and any other pertinent obligations, then as a consequence you may
-not convey it at all.  For example, if you agree to terms that obligate you
-to collect a royalty for further conveying from those to whom you convey
-the Program, the only way you could satisfy both those terms and this
-License would be to refrain entirely from conveying the Program.
-
-  13. Use with the GNU Affero General Public License.
-
-  Notwithstanding any other provision of this License, you have
-permission to link or combine any covered work with a work licensed
-under version 3 of the GNU Affero General Public License into a single
-combined work, and to convey the resulting work.  The terms of this
-License will continue to apply to the part which is the covered work,
-but the special requirements of the GNU Affero General Public License,
-section 13, concerning interaction through a network will apply to the
-combination as such.
-
-  14. Revised Versions of this License.
-
-  The Free Software Foundation may publish revised and/or new versions of
-the GNU General Public License from time to time.  Such new versions will
-be similar in spirit to the present version, but may differ in detail to
-address new problems or concerns.
-
-  Each version is given a distinguishing version number.  If the
-Program specifies that a certain numbered version of the GNU General
-Public License "or any later version" applies to it, you have the
-option of following the terms and conditions either of that numbered
-version or of any later version published by the Free Software
-Foundation.  If the Program does not specify a version number of the
-GNU General Public License, you may choose any version ever published
-by the Free Software Foundation.
-
-  If the Program specifies that a proxy can decide which future
-versions of the GNU General Public License can be used, that proxy's
-public statement of acceptance of a version permanently authorizes you
-to choose that version for the Program.
-
-  Later license versions may give you additional or different
-permissions.  However, no additional obligations are imposed on any
-author or copyright holder as a result of your choosing to follow a
-later version.
-
-  15. Disclaimer of Warranty.
-
-  THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
-APPLICABLE LAW.  EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
-HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
-OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
-THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
-PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
-IS WITH YOU.  SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
-ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
-
-  16. Limitation of Liability.
-
-  IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
-WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
-THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
-GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
-USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
-DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
-PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
-EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
-SUCH DAMAGES.
-
-  17. Interpretation of Sections 15 and 16.
-
-  If the disclaimer of warranty and limitation of liability provided
-above cannot be given local legal effect according to their terms,
-reviewing courts shall apply local law that most closely approximates
-an absolute waiver of all civil liability in connection with the
-Program, unless a warranty or assumption of liability accompanies a
-copy of the Program in return for a fee.
-
-              END OF TERMS AND CONDITIONS
-
-     How to Apply These Terms to Your New Programs
-
-  If you develop a new program, and you want it to be of the greatest
-possible use to the public, the best way to achieve this is to make it
-free software which everyone can redistribute and change under these terms.
-
-  To do so, attach the following notices to the program.  It is safest
-to attach them to the start of each source file to most effectively
-state the exclusion of warranty; and each file should have at least
-the "copyright" line and a pointer to where the full notice is found.
-
-    <one line to give the program's name and a brief idea of what it does.>
-    Copyright (C) <year>  <name of author>
-
-    This program is free software: you can redistribute it and/or modify
-    it under the terms of the GNU General Public License as published by
-    the Free Software Foundation, either version 3 of the License, or
-    (at your option) any later version.
-
-    This program is distributed in the hope that it will be useful,
-    but WITHOUT ANY WARRANTY; without even the implied warranty of
-    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-    GNU General Public License for more details.
-
-    You should have received a copy of the GNU General Public License
-    along with this program.  If not, see <http://www.gnu.org/licenses/>.
-
-Also add information on how to contact you by electronic and paper mail.
-
-  If the program does terminal interaction, make it output a short
-notice like this when it starts in an interactive mode:
-
-    <program>  Copyright (C) <year>  <name of author>
-    This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
-    This is free software, and you are welcome to redistribute it
-    under certain conditions; type `show c' for details.
-
-The hypothetical commands `show w' and `show c' should show the appropriate
-parts of the General Public License.  Of course, your program's commands
-might be different; for a GUI interface, you would use an "about box".
-
-  You should also get your employer (if you work as a programmer) or school,
-if any, to sign a "copyright disclaimer" for the program, if necessary.
-For more information on this, and how to apply and follow the GNU GPL, see
-<http://www.gnu.org/licenses/>.
-
-  The GNU General Public License does not permit incorporating your program
-into proprietary programs.  If your program is a subroutine library, you
-may consider it more useful to permit linking proprietary applications with
-the library.  If this is what you want to do, use the GNU Lesser General
-Public License instead of this License.  But first, please read
-<http://www.gnu.org/philosophy/why-not-lgpl.html>.
+Template.Toolkit - Template Toolkit implementation for Haskell+Copyright (C) 2017 Dzianis Kabanau++This program is free software: you can redistribute it and/or modify+it under the terms of the GNU General Public License as published by+the Free Software Foundation, either version 3 of the License, or+(at your option) any later version.++This program is distributed in the hope that it will be useful,+but WITHOUT ANY WARRANTY; without even the implied warranty of+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the+GNU General Public License for more details.++You should have received a copy of the GNU General Public License+along with this program.  If not, see <https://www.gnu.org/licenses/>.
Setup.hs view
@@ -1,2 +1,2 @@-import Distribution.Simple
-main = defaultMain
+import Distribution.Simple+main = defaultMain
− Text/TemplateToolkit.hs
@@ -1,997 +0,0 @@-{-|
-Module      : Text.TemplateToolkit
-Description : Template Toolkit implementation for Haskell
-Copyright   : (c) Dzianis Kabanau, 2017
-Maintainer  : kobargh@gmail.com
-
-This is a Haskell implementation of <http://www.template-toolkit.org Template Toolkit> - the popular Perl template processing system.
-
--}
-
-module Text.TemplateToolkit (
-    -- * Documentation
-    TName
-    ,TErr
-    ,TConfig(..)
-    ,evalTemplateFile
-    -- $conf
-
-    -- * Example
-    -- $example
- ) where
-
-import Text.TemplateToolkitAST
-import Data.List
-import Data.Foldable (toList)
-import Control.Applicative ((<*))
-import Control.Monad.State
-import Control.Monad.Except
-import System.Directory
-import qualified Data.Map.Strict as Map
-import qualified Data.Sequence as Seq
-import qualified Data.HashMap.Lazy as HashMap (toList)
-
-import Data.Char (toUpper,toLower,isDigit)
-import qualified Data.Text as T
-import qualified Data.Text.IO as TIO (readFile)
-import qualified Data.Text.Encoding as TE (decodeUtf8)
-import qualified Data.Text.Lazy as TL (fromStrict)
-import qualified Data.Text.Lazy.Encoding as TLE (encodeUtf8)
-import qualified Data.ByteString.Char8 as BS
-
-import Text.Regex.PCRE
-import qualified Text.Regex.PCRE.ByteString.Utils as PCRE
-import qualified Network.URI.Encode as URI (encodeText)
-import qualified Data.Aeson as Aeson
-import Data.Scientific (floatingOrInteger)
-
-import Debug.Trace
-
-type TErr = String
-
-type EvalWithExcept = ExceptT TErr (StateT Env IO)
-    
-instance Eq Val where
-    (==) (VFloat x) (VFloat y) = x == y
-    (==) (VFloat x) (VInt y) = x == (fromInteger y)
-    (==) (VInt x) (VFloat y) = (fromInteger x) == y
-    (==) (VInt x) (VInt y) = x == y
-    (==) x@(VFloat _) y = x == (toNum y)
-    (==) x y@(VFloat _) = (toNum x) == y
-    (==) x@(VInt _) y = x == (toNum y)
-    (==) x y@(VInt _) = (toNum x) == y
-    (==) (VString a) (VString b) = a == b
-
-instance Ord Val where
-    compare (VFloat x) (VFloat y) = compare x y
-    compare (VFloat x) (VInt y) = compare x (fromInteger y)
-    compare (VInt x) (VFloat y) = compare (fromInteger x) y
-    compare (VInt x) (VInt y) = compare x y
-    compare (VString x) (VString y) = compare x y
-    compare x@(VString _) y@(VFloat _) = compare (toNum x) y
-    compare x@(VString _) y@(VInt _) = compare (toNum x) y
-    compare x@(VFloat _) y@(VString _) = compare x (toNum y)
-    compare x@(VInt _) y@(VString _) = compare x (toNum y)
-    
-instance Num Val where
-    (+) (VInt x) (VInt y) = VInt (x + y)
-    (+) (VInt x) (VFloat y) = VFloat (fromInteger x + y)
-    (+) (VFloat x) (VFloat y) = VFloat (x + y)
-    (+) (VFloat x) (VInt y) = VFloat (x + fromInteger y)
-    (+) x y = (toNum x) + (toNum y)
-    (*) (VInt x) (VInt y) = VInt (x * y)
-    (*) (VInt x) (VFloat y) = VFloat (fromInteger x * y)
-    (*) (VFloat x) (VFloat y) = VFloat (x * y)
-    (*) (VFloat x) (VInt y) = VFloat (x * fromInteger y)
-    (*) x y = (toNum x) * (toNum y)
-    abs (VInt x) = VInt (abs x)
-    abs (VFloat x) = VFloat (abs x)
-    abs x = abs (toNum x)
-    signum (VInt x) = VInt (signum x)
-    signum (VFloat x) = VFloat (signum x)
-    signum x = signum (toNum x)
-    fromInteger = VInt
-    negate (VInt x) = VInt (negate x)
-    negate (VFloat x) = VFloat (negate x)
-    negate x = negate (toNum x)
-    
-instance Fractional Val where
-    fromRational = VFloat . fromRational
-    (/) (VFloat x) (VFloat y) = VFloat (x / y)
-    (/) (VFloat x) (VInt y) = VFloat (x / fromInteger y)
-    (/) (VInt x) (VFloat y) = VFloat (fromInteger x / y)
-    (/) (VInt x) (VInt y) = VFloat (fromInteger x / fromInteger y)
-    (/) x y = (toNum x) / (toNum y)
-
-    
-fromAeson :: Aeson.Value -> Expr
-fromAeson Aeson.Null = EVal Undef
-fromAeson (Aeson.Bool True) = EVal $ VInt 1
-fromAeson (Aeson.Bool False) = EVal $ VInt 0
-fromAeson (Aeson.Number x) = case floatingOrInteger x of
-    (Left f) -> EVal . VFloat . realToFrac $ f
-    (Right i) -> EVal . VInt . fromInteger $ i
-fromAeson (Aeson.String s) = EVal $ VString s
-fromAeson (Aeson.Array a) = EVal . VArray . (map fromAeson) . toList $ a
-fromAeson (Aeson.Object o) = EVal . VHash . (map (\(k,v) -> (T.unpack k,fromAeson v))) . HashMap.toList $ o
-    
-refTableInsert :: Hash -> Val -> (String,Hash)
-refTableInsert refTableOrig v = (refId, refTableNew) where
-    refId = reftype ++ "ref#" ++ show (Map.size refTableOrig + 1)
-        where reftype = case v of
-                VHashV _ -> "hash"
-                VArrayV _ -> "array"
-                _ -> ""
-    refTableNew = Map.insert refId v refTableOrig
-    
-getFromHash :: String -> Hash -> Val
-getFromHash = Map.findWithDefault (Undef)
-
-getVal :: (String,Val) -> VarNode -> EvalWithExcept (String,Val)
-getVal (_,VRef r') k = do
-    (_,reft) <- get
-    getVal (r',getFromHash r' reft) k
-getVal (r,VHashV h) (VarKey k) = do
-    case Map.lookup k h of
-        Nothing -> case lookup k vmethods of
-            Nothing -> return (r,Undef)
-            Just m -> m (VHashV h) [] r
-        Just v -> return (r,v)
-getVal (r,VArrayV a) (VarIndex i) = return $ if Seq.length a <= (fromIntegral i) then (r,Undef) else (r,Seq.index a i)
-getVal (r,v) (VarMethod met pars) = case lookup met vmethods of
-    -- Nothing -> return (r,VString . T.pack $ "Invalid method '" ++ met ++ "'")
-    Nothing -> throwError ("Invalid method '" ++ met ++ "'")
-    Just m -> m v pars r
-getVal (r,v) (VarKey k) = do
-    case lookup k vmethods of
-        Nothing -> return (r,Undef)
-        Just m -> m v [] r
-getVal (r,_) _ = return (r,Undef)
-    
-getVar :: Var -> EvalWithExcept (String,Val)
-getVar (Var ((VarKey k0):vs)) = do
-    (vars,reft) <- get
-    getVar' ("",getFromHash k0 vars) vs
-    where getVar' (r,val) (v':vs') = do
-                k' <- getNode v'
-                (r',v') <- getVal (r,val) k'
-                getVar' (r',v') vs'
-          getVar' (r,val) [] = return (r,val)
-          getNode (VarRef r) = do
-                val <- getVar r
-                case val of
-                    (_,VInt i) -> return $ VarIndex (fromIntegral i)
-                    (_,v) -> return $ VarKey (toString v)
-          getNode n = return n
-
-getVarVal :: Var -> EvalWithExcept Val
-getVarVal var = do
-    (_,val) <- getVar var
-    case val of
-        (VRef r) -> do
-            (_,reft) <- get
-            return $ getFromHash r reft
-        _ -> return val
-          
-toNum :: Val -> Val
-toNum (VString s) = case reads (T.unpack s) :: [(Integer,String)] of
-    [] -> case reads (T.unpack s) :: [(Double,String)] of
-        [] -> VInt 0
-        (s',_):_ -> VFloat s'
-    (s',_):_ -> VInt s'
-toNum v@(VInt _) = v
-toNum v@(VFloat _) = v
-toNum _  = VInt 0
-
-toInt :: Val -> Int
-toInt (VFloat v) = fromIntegral . truncate $ v
-toInt (VInt v) = fromIntegral v
-toInt v = toInt . toNum $ v
-
-toString :: Val -> String
-toString (VString v) = T.unpack v
-toString (VInt v) = show v
-toString (VFloat v) = show v
-toString (VRef v) = v
-toString _ = ""
-
-toText :: Val -> T.Text
-toText (VString v) = v
-toText v = T.pack . toString $ v
-
-not' :: Val -> Val
-not' (VString s) = if (T.unpack s) == "" then VInt 1 else VInt 0
-not' (VInt 0) = VInt 1
-not' (VFloat 0.0) = VInt 1
-not' (Undef) = VInt 1
-not' _ = VInt 0
-
-evalUnOp :: UnOp -> Expr -> EvalWithExcept Val
-evalUnOp Pos e = liftM toNum (evalExpr e)
-evalUnOp Neg e = liftM (negate . toNum) (evalExpr e)
-evalUnOp Not e = liftM not' (evalExpr e)
-
-isValTrue :: Val -> Bool
-isValTrue v = not' v == VInt 0
-
-evalBinOp' :: (Val -> Val -> Val) -> Expr -> Expr -> EvalWithExcept Val
-evalBinOp' f e1 e2 = liftM2 f (evalExpr e1) (evalExpr e2)
-evalBinBoolOp' :: (Val -> Val -> Bool) -> Expr -> Expr -> EvalWithExcept Val
-evalBinBoolOp' f e1 e2 = do
-    x' <- evalExpr e1
-    y' <- evalExpr e2
-    case f x' y' of
-        True -> return $ VInt 1
-        False -> return $ VInt 0
-evalBinOp :: BinOp -> Expr -> Expr -> EvalWithExcept Val
-evalBinOp Add = evalBinOp' (+)
-evalBinOp Sub = evalBinOp' (-)
-evalBinOp Mul = evalBinOp' (*)
-evalBinOp Div = div'
-    where div' x y = do
-            x' <- evalExpr x
-            y' <- evalExpr y
-            if (toInt y') == 0 then throwError "Division by zero" else return (x' / y')
-evalBinOp Mod = mod'
-    where mod' x y = do
-                x' <- evalExpr x
-                y' <- evalExpr y
-                mod'' x' y'
-          mod'' :: Val -> Val -> EvalWithExcept Val
-          mod'' (VInt x) (VInt y) = if y /= 0 then (return $ VInt (x `mod` y)) else throwError "Division by zero"
-          mod'' _ _ = throwError "Not integer in 'mod' operation"
-evalBinOp Con = con'
-    where con' x y = do
-            x' <- evalExpr x
-            y' <- evalExpr y
-            return $ VString (toText x' `T.append` toText y')
-evalBinOp Gt = evalBinBoolOp' (>)
-evalBinOp Ge = evalBinBoolOp' (>=)
-evalBinOp Lt = evalBinBoolOp' (<)
-evalBinOp Le = evalBinBoolOp' (<=)
-evalBinOp Eq = evalBinBoolOp' (==)
-evalBinOp Ne = evalBinBoolOp' (/=)
-evalBinOp And = and'
-    where and' e1 e2 = do
-            v1' <- evalExpr e1
-            if isValTrue v1'
-            then do
-                v2' <- evalExpr e2
-                if isValTrue v2'
-                then return v2'
-                else return $ VInt 0
-            else return $ VInt 0
-evalBinOp Or = or'
-    where or' e1 e2 = do
-            v1' <- evalExpr e1
-            if isValTrue v1'
-            then return v1'
-            else do
-                v2' <- evalExpr e2
-                if isValTrue v2'
-                then return v2'
-                else return $ VInt 0
-
-evalExpr :: Expr -> EvalWithExcept Val
-evalExpr (EVal (VArray xs)) = do
-    xs' <- mapM evalExprWithRef xs
-    return $ VArrayV $ Seq.fromList xs'
-evalExpr (EVal (VArrayRange e1 e2)) = do
-    from' <- evalExpr e1
-    to' <- evalExpr e2
-    return $ VArrayV . Seq.fromList . (map (VInt . fromIntegral)) $ [(toInt from')..(toInt to')]
-evalExpr (EVal a@(VArrayV _)) = do
-    return a
-evalExpr (EVal (VHash xs)) = do
-    let (ks',es') = unzip xs
-    vs' <- mapM evalExprWithRef es'
-    return $ VHashV (Map.fromList (zip ks' vs'))
-evalExpr (EVal (VIString vs)) = do
-    let getVIString (IString s) = return s
-        getVIString (IVar v) = liftM (toText . snd) (getVar v)
-    vs' <- mapM getVIString vs
-    return $ VString (T.concat vs')
-    
-evalExpr (EVal v) = return v
-evalExpr (EVar var) = do
-    (_,val) <- getVar var
-    return val
-evalExpr (EUnOp op e) = evalUnOp op e
-evalExpr (EBinOp op e1 e2) = evalBinOp op e1 e2
-evalExpr (ETerOp e1 e2 e3) = do
-    v1' <- evalExpr e1
-    if isValTrue v1'
-    then evalExpr e2
-    else evalExpr e3
-
-evalExprWithRef :: Expr -> EvalWithExcept Val
-evalExprWithRef e = do
-    v <- evalExpr e
-    case v of
-        a@(VArrayV _) -> do
-            (vars,reft) <- get
-            let (id,reft') = refTableInsert reft a
-            put (vars,reft')
-            return $ VRef id
-        h@(VHashV _) -> do
-            (vars,reft) <- get
-            let (id,reft') = refTableInsert reft h
-            put (vars,reft')
-            return $ VRef id
-        x -> return x
-
---
------ statement evaluators
---
-
-fst3 :: (a,b,c) -> a
-fst3 (x,_,_) = x
-
-snd3 :: (a,b,c) -> b
-snd3 (_,x,_) = x
-
-trd3 :: (a,b,c) -> c
-trd3 (_,_,x) = x
-
-evalOrSkipStmt :: Stmt -> EvalWithExcept T.Text
-evalOrSkipStmt stmt = do
-    (_,reft) <- get
-    let (VHashV loops) = Map.findWithDefault (VHashV Map.empty) "#loopcontrols" reft
-    let (VInt breakCode) = Map.findWithDefault (VInt 0) (show . fst3 . sId $ stmt) loops
-    if breakCode == 0
-    then evalStmt stmt
-    else return T.empty
-
-evalStmt :: Stmt -> EvalWithExcept T.Text
-
-evalStmt (Seq ss stmtId _) = do
-    ss' <- mapM evalOrSkipStmt ss
-    return $ T.concat ss'
-
-evalStmt (SComment _ _) = return T.empty
-    
-evalStmt (SText s stmtId _) = return s
-
-evalStmt (SExpr e stmtId _) = do
-    val <- evalExpr e
-    return $ toText val
-    
-evalStmt assign@(SAssign (Var varPath@((VarKey k0):vs)) e stmtId lineN) = do
-    val <- evalExprWithRef e
-    if null vs
-    then do
-        modify (\(vars,reft) -> (Map.insert k0 val vars,reft))
-        return T.empty
-    else do
-        var <- getVar (Var $ init varPath)
-        updateRefTable var (last varPath) val
-            where updateRefTable :: (String,Val) -> VarNode -> Val -> EvalWithExcept T.Text
-                  updateRefTable (r,VHashV h) (VarKey k) val = do
-                        modify (\(vars,reft) -> (vars,Map.insert r (VHashV $ Map.insert k val h) reft))
-                        return T.empty
-                  updateRefTable (r,VHashV h) (VarMethod "item" (e':_)) val = do
-                        k' <- evalExpr e'
-                        modify (\(vars,reft) -> (vars,Map.insert r (VHashV $ Map.insert (toString k') val h) reft))
-                        return T.empty
-                  updateRefTable (r,VArrayV a) (VarIndex i) val = do
-                        modify (\(vars,reft) -> (vars,Map.insert r (VArrayV $ updateArr' i val a) reft))
-                        return T.empty
-                        where updateArr' i val a = if (Seq.length a > i) then (Seq.update i val a)
-                                                   else (a Seq.>< (Seq.replicate (i - Seq.length a) (Undef))) Seq.|> val
-                  updateRefTable (_,VRef r) vark val = do
-                        (_,reft) <- get
-                        updateRefTable (r,getFromHash r reft) vark val
-                        return T.empty
-                  -- auto-vivification
-                  updateRefTable _ k@(VarKey _) _ = do
-                        evalStmt $ SAssign (Var $ init varPath) (EVal (VHashV Map.empty)) stmtId lineN
-                        evalStmt assign
-                  updateRefTable _ k@(VarMethod "item" _) _ = do
-                        evalStmt $ SAssign (Var $ init varPath) (EVal (VHashV Map.empty)) stmtId lineN
-                        evalStmt assign
-                  updateRefTable _ k@(VarIndex _) _ = do
-                        evalStmt $ SAssign (Var $ init varPath) (EVal (VArrayV Seq.empty)) stmtId lineN
-                        evalStmt assign
-                  -- otherwise
-                  updateRefTable v k val = throwError (show lineN ++ ": Variable assign error:\nvariable path: " ++ (show varPath) ++ "\nvalue: " ++ (show val) ++ "\n")
-    
-evalStmt (SIf cond body maybeElse stmtId _) = do
-    v' <- evalExpr cond
-    if isValTrue v'
-    then evalOrSkipStmt body
-    else evalElse' maybeElse
-        where evalElse' Nothing = return T.empty
-              evalElse' (Just (Elsif cond' body' maybeElse')) = do
-                    v'' <- evalExpr cond'
-                    if isValTrue v''
-                    then evalOrSkipStmt body'
-                    else evalElse' maybeElse'
-              evalElse' (Just (Else body'')) = evalOrSkipStmt body''
-          
-evalStmt whileStmt@(SWhile cond body stmtId _) = do
-    isSkip <- loopStart (snd3 . sId $ whileStmt)
-    val <- evalExpr cond
-    if isValTrue val && (not isSkip)
-    then do
-        v' <- evalOrSkipStmt body
-        v'' <- evalOrSkipStmt whileStmt
-        return $ T.append v' v''
-    else return T.empty
-    
-evalStmt foreachStmt@(SForeach var expr body stmtId lineN) = do
-    isSkip <- loopStart (snd3 . sId $ foreachStmt)
-    val <- evalExpr expr
-    foreach' val isSkip <* loopEnd (sId foreachStmt)
-        where foreach' :: Val -> Bool -> EvalWithExcept T.Text
-              foreach' (VRef r') False = do
-                    (_,reft) <- get
-                    foreach' (getFromHash r' reft) False
-              foreach' (VArrayV a') False = case toList a' of
-                    [] -> return T.empty
-                    (x:xs) -> do
-                        evalIterator (snd3 stmtId) a' (+1)
-                        evalStmt (SAssign var (EVal x) stmtId lineN)
-                        v' <- evalStmt body
-                        isSkip' <- loopStart (snd3 . sId $ foreachStmt)
-                        v'' <- foreach' (VArrayV $ Seq.fromList xs) isSkip'
-                        return $ T.append v' v''
-              foreach' x False = foreach' (VArrayV $ Seq.singleton x) False
-              foreach' _ True = return T.empty
-                    
-evalStmt (SBlock name body stmtId _) = do
-    (vars,reft) <- get
-    let (VHashV coderefs) = Map.findWithDefault (VHashV Map.empty) "#coderefs" reft
-    let coderefs' = Map.insert name (VCode body) coderefs
-    put (vars,Map.insert "#coderefs" (VHashV coderefs') reft)
-    return T.empty
-
-evalStmt (SProcess name assigns stmtId _) = do
-    mapM evalOrSkipStmt assigns
-    (VString name') <- evalExpr (EVal (VIString [name]))
-    evalNamedBlock (T.unpack name',stmtId)
-
-evalStmt (SWrapper name assigns body stmtId lineN) = do
-    (VString name') <- evalExpr (EVal (VIString [name]))
-    mapM evalOrSkipStmt assigns >> return T.empty
-    content <- evalOrSkipStmt body
-    evalStmt $ SAssign (Var [VarKey "content"]) (EVal (VString content)) (0,0,0) lineN
-    evalNamedBlock (T.unpack name',stmtId)
-
-evalStmt (SFilter name exprs body stmtId _) = do
-    v <- evalStmt body
-    case lookup name filters of
-        Nothing -> throwError ("Invalid filter '" ++ name ++ "'")
-        Just f -> f exprs v
-                          
-evalStmt (SLast (parent,_,_) _) = do
-    evalLastNext parent (-1)
-
-evalStmt (SNext (parent,_,_) _) = do
-    evalLastNext parent 1
-
-evalNamedBlock :: (String,StmtId) -> EvalWithExcept T.Text
-evalNamedBlock (name,stmtId) = do
-    env@(_,reft) <- get
-    let (VHashV coderefs) = Map.findWithDefault (VHashV Map.empty) "#coderefs" reft
-    case Map.lookup name coderefs of
-        (Just (VCode body)) -> evalOrSkipStmt body
-        Nothing -> do
-            t <- getVarVal (Var [VarKey "_CONFIG", VarKey "TEMPLATES", VarKey name])
-            case t of
-                Undef -> throwError ("\"" ++ name ++ "\" not found")
-                tn -> do
-                    tf <- liftIO $ TIO.readFile (toString tn)
-                    let stmtId' = (fst3 stmtId, (trd3 stmtId) + 1, (trd3 stmtId) + 1)
-                    case parseTemplateWithStmtId tf name stmtId' of
-                        Left err -> throwError (name ++ ": " ++ (show err))
-                        Right stmt -> do
-                            evalStmt (SBlock (toString tn) stmt stmtId' 0)
-                            catchError (evalOrSkipStmt stmt) (\e -> throwError (name ++ ": " ++ e))
-
-    
-evalLastNext :: Int -> Int -> EvalWithExcept T.Text
-evalLastNext parent breakCode = do
-    (vars,reft) <- get
-    let (VHashV loops) = Map.findWithDefault (VHashV Map.empty) "#loopcontrols" reft
-    let loops' = Map.insert (show parent) (VInt $ fromIntegral breakCode) loops
-    put (vars,Map.insert "#loopcontrols" (VHashV loops') reft)
-    return T.empty
-    
-loopStart :: Int -> EvalWithExcept Bool
-loopStart loopId = do
-    (vars,reft) <- get
-    let (VHashV loops) = Map.findWithDefault (VHashV Map.empty) "#loopcontrols" reft
-        (VInt breakCode) = Map.findWithDefault (VInt 0) (show loopId) loops
-        loops' = Map.insert (show loopId) (VInt $ if breakCode /= (-1) then 0 else (-1)) loops
-    put (vars,Map.insert "#loopcontrols" (VHashV loops') reft)
-    return $ if breakCode == (-1) then True else False
-    
-loopEnd :: StmtId -> EvalWithExcept ()
-loopEnd loopId = do
-    modify (\(vars,reft) -> (vars,Map.delete ("#foreacharr" ++ (show $ snd3 loopId)) reft))
-    modify (\(vars,reft) -> (vars,Map.delete ("#foreach" ++ (show $ snd3 loopId)) reft))
-    (vars,reft) <- get
-    let (VHashV loops) = Map.findWithDefault (VHashV Map.empty) "#loopcontrols" reft
-        loops' = Map.insert (show $ snd3 loopId) (VInt 0) loops
-        reft' = Map.insert "#loopcontrols" (VHashV loops') reft
-        (VArrayV outer) = Map.findWithDefault (VArrayV Seq.empty) ("#foreacharr" ++ (show $ fst3 loopId)) reft'
-    if Seq.null outer then put (Map.delete "loop" vars,reft') >> return ()
-    else put (vars,reft') >> evalIterator (fst3 loopId) outer id
-    
-evalIterator :: Int -> Array -> (Val -> Val) -> EvalWithExcept ()
-evalIterator loopId arr iterFunc = do
-    (vars,reft) <- get
-    let l' = fromIntegral $ Seq.length arr
-        start = [("size",VInt l')
-                ,("max",VInt $ l' - 1)
-                ,("index",VInt (-1))
-                ]
-        (VHashV h') = Map.findWithDefault (VHashV $ Map.fromList start) ("#foreach" ++ show loopId) reft
-        (VArrayV a') = Map.findWithDefault (VArrayV arr) ("#foreacharr" ++ show loopId) reft
-        (VInt i) = iterFunc (h' Map.! "index")
-        (VInt iMax) = h' Map.! "max"
-        h = flip Map.union h' (Map.fromList [("index",VInt i)
-                                           ,("count",VInt i+1)
-                                           ,("first",if i == 0 then VInt 1 else VInt 0)
-                                           ,("last",if i == iMax then VInt 1 else VInt 0)
-                                           ,("prev",if i == 0 then Undef else (a' `Seq.index` (fromIntegral i - 1)))
-                                           ,("next",if i == iMax then Undef else (a' `Seq.index` (fromIntegral i + 1)))
-                                           ,("odd",if (i + 1) `mod` 2 == 0 then VInt 0 else VInt 1)
-                                           ,("even",if (i + 1) `mod` 2 == 0 then VInt 1 else VInt 0)
-                                           ])
-        reft' = Map.insert ("#foreach" ++ show loopId) (VHashV h) reft
-        reft'' = Map.insert ("#foreacharr" ++ show loopId) (VArrayV a') reft'
-        vars'' = Map.insert "loop" (VHashV h) vars
-    put (vars'',reft'')
-    return ()
-
---
------ /statement evaluators
---
-
-
---
------ common functions for vmethods and filters
---
-
-_changefirst' s change = case T.unpack s of
-    x:xs -> T.pack $ (change x):xs
-    [] -> T.empty
-_Lcfirst' s = _changefirst' s toLower
-_Lower' = T.toLower
-_Ucfirst' s = _changefirst' s toUpper
-_Upper' = T.toUpper
-_Replace' s re su = case PCRE.substituteCompile' (BS.pack re) (BS.pack . T.unpack $ s) (BS.pack su) of
-                        Left err -> error err
-                        Right bs -> TE.decodeUtf8 $ bs
-_Trim' s = _Replace' s "(^\\s+|\\s+$)" ""
-_Collapse' s = _Trim' $ _Replace' s "\\s+" " "
-
---
------ /common functions for vmethods and filters
---
-
---
------ vmethods
---
-
-vmethods =
-    [("collapse",_collapse)
-    ,("defined",_defined)
-    ,("delete",_delete)
-    ,("each",_values)
-    ,("first",_first)
-    ,("grep",_grep)
-    ,("import",_import)
-    ,("item",_item)
-    ,("join",_join)
-    ,("keys",_keys)
-    ,("last",_last)
-    ,("lcfirst",_lcfirst)
-    ,("length",_length)
-    ,("lower",_lower)
-    ,("match",_match)
-    ,("nsort",_nsort)
-    ,("pairs",_pairs)
-    ,("pop",_pop)
-    ,("push",_push)
-    ,("remove",_remove)
-    ,("replace",_replace)
-    ,("reverse",_reverse)
-    ,("shift",_shift)
-    ,("size",_size)
-    ,("slice",_slice)
-    ,("sort",_sort)
-    ,("splice",_splice)
-    ,("split",_split)
-    ,("trim",_trim)
-    ,("ucfirst",_ucfirst)
-    ,("unique",_unique)
-    ,("unshift",_unshift)
-    ,("upper",_upper)
-    ,("values",_values)
-    
-    ] where
-    getter v _ r = return (r,v)
-    
-    _match' :: Val -> String -> [Val]
-    _match' v re = map (VString . TE.decodeUtf8) matches
-        where bs = (BS.pack . toString $ v) =~ re :: AllTextSubmatches [] BS.ByteString
-              matches = case getAllTextSubmatches bs of
-                    [] -> []
-                    (full:[]) -> [full]
-                    (_:subs) -> subs
-        
-    _matchGlobal' :: Val -> String -> [Val]
-    _matchGlobal' v re = case _match' (VString $ T.pack re) "\\(.+\\)" of
-        [] -> matches 
-        _ -> concat $ map (\m' -> _match' m' re) matches
-        where matches = map (VString . TE.decodeUtf8) $ getAllTextMatches ((BS.pack . toString $ v) =~ re :: AllTextMatches [] BS.ByteString)
-
-    _size' (VArrayV a) = fromIntegral $ Seq.length a
-    
-    _splice' a offs leng arr = (Seq.take offs'' a) Seq.>< arr Seq.>< (Seq.drop (offs'' + leng'') a)
-        where l = Seq.length a
-              offs' = if offs < 0 then l + offs + 1 else offs
-              offs'' = if offs' < 0 then 0 else offs'
-              leng' = if leng < 0 then (l - offs'' + leng) else leng
-              leng'' = if leng' < 0 then 0 else leng'
- 
-    _collapse s _ r = return (r, VString . _Collapse' . toText $ s)
-    
-    _defined h@(VHashV _) (k:_) r = do
-        k' <- evalExpr k
-        (r',v) <- getVal (r,h) (VarKey . toString $ k')
-        _defined v [] r
-    _defined a@(VArrayV _) (i:_) r = do
-        i' <- evalExpr i
-        (r',v) <- getVal (r,a) (VarIndex . toInt $ i')
-        _defined v [] r
-    _defined Undef _ r = return (r,VInt 0)
-    _defined _ _ r = return (r,VInt 1)
-    
-    _delete _ [] r = return (r,Undef)
-    _delete (VHashV h) (e:es) r = do
-        k <- evalExpr e
-        let vh = VHashV $ Map.delete (toString k) h
-        modify (\(vars,reft) -> (vars,Map.insert r vh reft))
-        _delete vh es r
-    
-    _each (VHashV h) _ r = do
-        return (r,arr)
-            where arr = VArrayV . Seq.fromList . concat . map (\(k,v) -> [(VString . T.pack $ k),v]) $ (Map.toList h)
-    _each _ _ r = return (r,Undef)
-    
-    _first a@(VArrayV a') [] r = if (_size' a) > 0 then return (r,a' `Seq.index` 0) else return (r,Undef)
-    _first (VArrayV a) (e:_) r = do
-        i <- evalExpr e
-        return (r, VArrayV $ Seq.take (toInt i) a)
-    _first _ _ r = return (r,Undef)
-    
-    _grep (VArrayV a) (re:_) r = do
-        re' <- liftM toString (evalExpr re)
-        let a' = filter (\v -> not . null $ _match' v re') (toList a)
-        return (r, VArrayV . Seq.fromList $ a')
-    
-    _import (VHashV h) (e:es) r = do
-        v <- evalExpr e
-        case v of
-            (VRef r') -> do
-                (_,reft) <- get
-                let v' = reft Map.! r'
-                _import (VHashV h) ((EVal v'):es) r
-            (VHashV h') -> do
-                let vh = VHashV $ Map.union h' h
-                modify (\(vars,reft) -> (vars,Map.insert r vh reft))
-                _import vh es r
-            _ -> _import (VHashV h) es r
-    _import (VArrayV a) (e:es) r = do
-        v <- evalExpr e
-        case v of
-            (VRef r') -> do
-                (_,reft) <- get
-                let v' = reft Map.! r'
-                _import (VArrayV a) ((EVal v'):es) r
-            (VArrayV a') -> do
-                let va = VArrayV $ a Seq.>< a'
-                modify (\(vars,reft) -> (vars,Map.insert r va reft))
-                _import va es r
-            _ -> _import (VArrayV a) es r
-    _import _ _ r = return (r,Undef)
-            
-    _item h@(VHashV _) (e:_) r = do
-        k <- evalExpr e
-        (_,v) <- getVal (r,h) (VarKey .toString $ k)
-        return (r,v)
-    _item _ _ r = return (r,Undef)
-        
-    _join (VArrayV a) (e:_) r = do
-        delim <- evalExpr e
-        return(r,VString . T.pack . (intercalate $ toString delim) . (map toString) . toList $ a)
-    _join a@(VArrayV _) [] r = _join a [(EVal . VString . T.pack $ ",")] r
-    _join _ _ r = return (r,Undef)
-    
-    _keys (VHashV h) _ r = do
-        return (r,arr)
-            where arr = VArrayV . Seq.fromList . map (VString . T.pack) $ (Map.keys h)
-    _keys _ _ r = return (r,Undef)
-    
-    _last a@(VArrayV a') [] r = if (_size' a) > 0 then return (r,a' `Seq.index` ((_size' a) - 1)) else return (r,Undef)
-    _last (VArrayV a) (e:_) r = do
-        i <- evalExpr e
-        return (r, VArrayV . Seq.reverse $ Seq.take (toInt i) (Seq.reverse a))
-    _last _ _ r = return (r,Undef)
-    
-    _lcfirst s = getter $ VString (_Lcfirst' . toText $ s)
-
-    _length s = getter $ VInt (fromIntegral . length . toString $ s)
-
-    _lower s = getter $ VString (_Lower' . toText $ s)
-    
-    _match v (re:gl:_) r = do
-        re' <- liftM toString (evalExpr re)
-        gl' <- evalExpr gl
-        let matcher = if isValTrue gl' then _matchGlobal' else _match'
-        case matcher v re' of
-            [] -> return (r,Undef)
-            ms -> do
-                return (r,VArrayV . Seq.fromList $ ms)
-    _match v (re:[]) r = _match v [re, EVal . VInt $ 0] r
-    _match _ _ r = return (r,Undef)
-            
-    _nsort (VArrayV a) _ r = do
-        return (r,arr)
-            where arr = VArrayV . Seq.fromList . sort . (map toNum) . toList $ a
-    _nsort _ _ r = return (r,Undef)
-    
-    _pairs (VHashV h) _ r = do
-        return (r,arr)
-            where arr = VArrayV . Seq.fromList . map
-                        (\(k,v) -> VHashV . Map.fromList $ [("key",VString . T.pack $ k),("value",v)]) $ Map.toList h
-    _pairs _ _ r = return (r,Undef)
-    
-    _pop (VArrayV a) [] r = do
-        (_,v) <- _last (VArrayV a) [] r
-        let a' = VArrayV $ _splice' a (-2) 1 Seq.empty
-        modify (\(vars,reft) -> (vars,Map.insert r a' reft))
-        return (r,v)
-    _pop _ _ r = return (r,Undef)
-
-    _push (VArrayV a) exs r = do
-        vxs <- mapM evalExpr exs
-        let a' = VArrayV $ _splice' a (-1) 0 (Seq.fromList vxs)
-        modify (\(vars,reft) -> (vars,Map.insert r a' reft))
-        return (r,Undef)
-    _push _ _ r = return (r,Undef)
-
-    _remove v (re:_) r = _replace v [re] r
-    _remove _ _ r = return (r,Undef)
-    
-    _replace v (re:su:_) r = do
-        re' <- liftM toString (evalExpr re)
-        su' <- liftM toString (evalExpr su)
-        let v' = _Replace' (toText v) re' su'
-        return (r,VString v')
-    _replace v (re:_) r = _replace v [re, EVal . VString $ T.empty] r
-    _replace _ _ r = return (r,Undef)
-    
-    _reverse (VArrayV a) [] r = do
-        return (r,VArrayV . Seq.reverse $ a)
-    _reverse _ _ r = return (r,Undef)
-    
-    _shift (VArrayV a) [] r = do
-        (_,v) <- _first (VArrayV a) [] r
-        let a' = VArrayV $ _splice' a 0 1 Seq.empty
-        modify (\(vars,reft) -> (vars,Map.insert r a' reft))
-        return (r,v)
-    _shift _ _ r = return (r,Undef)
-    
-    _size (VHashV h) _ r = return (r,VInt . fromIntegral . length . toList $ h)
-    _size a@(VArrayV _) _ r = return (r,VInt (_size' a))
-    _size _ _ r = return (r,VInt 0)
-    
-    _slice (VArrayV a) (fr:to:_) r = do
-        fr' <- liftM toInt (evalExpr fr)
-        to' <- liftM toInt (evalExpr to)
-        let l = Seq.length a
-            fr'' = if fr' < 0 then l + fr' else fr'
-            to'' = if to' < 0 then l + to' else to'
-            a' = if fr'' < 0 || to'' < 0 || fr'' > to'' || fr'' > l || to'' > l then Seq.empty
-                 else (Seq.take (to'' - fr'' + 1)) . (Seq.drop fr'') $ a
-        return (r,VArrayV a')
-    _slice (VArrayV a) (fr:_) r = _slice (VArrayV a) [fr,EVal . VInt $ (-1)] r
-    _slice _ _ r = _slice (VArrayV Seq.empty) (map (EVal . VInt) [0,0]) r
-            
-        
-    _sort (VArrayV a) _ r = do
-        return (r,arr)
-            where arr = VArrayV . Seq.fromList . sort . (map (VString . T.pack . toString)) . toList $ a
-    _sort _ _ r = return (r,Undef)
-    
-    _splice (VArrayV a) (eo:el:exs) r = do
-        offs <- liftM toInt (evalExpr eo)
-        leng <- liftM toInt (evalExpr el)
-        vxs <- mapM evalExpr exs
-        arr <- mapM mkArrEl vxs
-        let arr' = VArrayV $ _splice' a offs leng (Seq.fromList $ concat arr)
-        modify (\(vars,reft) -> (vars,Map.insert r arr' reft))
-        return (r,Undef)
-            where mkArrEl :: Val -> EvalWithExcept [Val]
-                  mkArrEl (VRef r') = do
-                        (_,reft) <- get
-                        mkArrEl (reft Map.! r')
-                  mkArrEl (VArrayV a') = return (toList a')
-                  mkArrEl v = return [v]
-    _splice a@(VArrayV a') (eo:[]) r = _splice a [eo,(EVal . VInt . fromIntegral . Seq.length $ a')] r
-    _splice a@(VArrayV a') [] r = _splice a [EVal (VInt 0)] r
-    _splice _ _ r = return (r,Undef)
-    
-    _split v (re:_) r = do
-        re' <- liftM toString (evalExpr re)
-        let a = case PCRE.splitCompile' (BS.pack re') (BS.pack . toString $ v) of
-                    Left err -> error err
-                    Right vs -> VArrayV . Seq.fromList . (map (VString . TE.decodeUtf8)) $ vs
-        return (r,a)
-    _split v _ r = return (r,VArrayV . Seq.singleton . VString . toText $ v)
-    
-    _trim s _ r = return (r, VString . _Trim' . toText $ s)
-
-    _ucfirst s = getter $ VString (_Ucfirst' . toText $ s)
-    
-    _unique (VArrayV a) _ r = do
-        return (r,arr)
-            where arr = VArrayV . Seq.fromList . (nubBy (\x y -> toString x == toString y)) . toList $ a
-
-    _unshift (VArrayV a) exs r = do
-        vxs <- mapM evalExpr exs
-        let a' = VArrayV $ _splice' a 0 0 (Seq.fromList vxs)
-        modify (\(vars,reft) -> (vars,Map.insert r a' reft))
-        return (r,Undef)
-    _unshift _ _ r = return (r,Undef)
-
-    _upper s = getter $ VString (_Upper' . toText $ s)
-
-    _values (VHashV h) _ r = do
-        return (r, VArray . map EVal $ (Map.elems h))
-    _values _ _ r = return (r,Undef)
-
-
---
------ /vmethods
---
-
-
---
------ filters
---
-
-filters =
-    [("collapse",_collapse)
-    ,("html",_html)
-    ,("lcfirst",_lcfirst)
-    ,("lower",_lower)
-    ,("null",_null)
-    ,("replace",_replace)
-    ,("trim",_trim)
-    ,("ucfirst",_ucfirst)
-    ,("upper",_upper)
-    ,("uri",_uri)
-    ] where
-    _collapse _ v = return $ _Collapse' v
-    _html _ s = return $ foldl' (\s' (re,su) -> _Replace' s' re su) s [("&","&amp;"),("<","&lt;"),(">","&gt;"),("\"","&quot;")]
-    _lcfirst _ v = return $ _Lcfirst' v
-    _lower _ v = return $ _Lower' v
-    _null _ _ = return T.empty
-    _replace [] v = return v
-    _replace (re:[]) v = _replace [re, EVal . VString $ T.empty] v
-    _replace (re:su:_) v = do
-        re' <- liftM toString (evalExpr re)
-        su' <- liftM toString (evalExpr su)
-        let v' = _Replace' v re' su'
-        return v'
-    _trim _ v = return $ _Trim' v
-    _ucfirst _ v = return $ _Ucfirst' v
-    _upper _ v = return $ _Upper' v
-    _uri _ v = return $ URI.encodeText v
-
---
------ /filters
---
-    
-data TConfig = AesonObject Aeson.Value | JSONstring T.Text
-
-evalTemplateFile ::     TName -- ^ Template filename to process
-                        -> TConfig -- ^ Template config and initial variables (either aeson object or JSON object string)
-                        -> IO (Either TErr T.Text) -- ^ Result of template evaluation - either error or text
-evalTemplateFile t cfg = do
-    case cfg of
-        (JSONstring json) -> case Aeson.decode (TLE.encodeUtf8 . TL.fromStrict $ json) of
-                                (Just aeson) -> evalTemplateFile t (AesonObject aeson)
-                                _ -> return $ Left "Invalid JSON string"
-        (AesonObject aeson) -> case fromAeson aeson of
-                                (EVal (VHash h)) -> evalStateT (runExceptT (evalTemplateFile' t h)) (Map.empty,Map.empty)
-                                _ -> return $ Left "Aeson 'object' expected as a second parameter"
-                                
-
-
-evalTemplateFile' :: TName -> [(String,Expr)] -> EvalWithExcept T.Text
-evalTemplateFile' t cfg = do
-    let assignVar (k,e) = evalStmt (SAssign (Var [VarKey k]) e (0,0,0) 0)
-    mapM_ assignVar cfg
-    vDirs <- getVarVal (Var [VarKey "_CONFIG", VarKey "INCLUDE_PATH"])
-    let dirs = case vDirs of
-                (VArrayV a) -> toList a
-                s@(VString _) -> [s]
-                _ -> []
-        getFilesPaths d = do
-                f' <- liftM (map (\f_ -> (f_,d ++ "/" ++ f_))) (getDirectoryContents d)
-                f'' <- filterM (doesFileExist . snd) f'
-                return f''
-    files <- mapM (liftIO . getFilesPaths) (map toString dirs)
-    let files' = map (\(k,v) -> (k, VString . T.pack $ v)) (concat files)
-    evalStmt $ SAssign (Var [VarKey "_CONFIG", VarKey "TEMPLATES"]) (EVal (VHashV . Map.fromList $ files')) (0,0,0) 0
-    evalStmt $ SProcess (IString $ T.pack t) [] (0,0,0) 0
-    
-{-$conf
-All variables in initial 'TConfig' object are passed to the parsed template.
-
-Special /__\_CONFIG__/ variable is an object that contains settings passed to the template evaluator. For now there is only one setting: /__INCLUDE_PATH__/ - list of folders where evaluator will look for template files.
--}
-
-{-$example
-Template Toolkit language manual: "Text.TemplateToolkitLang".
-
-Below is a simple example of using this module:
-
-=== template-toolkit-example.hs
-@
-import Text.TemplateToolkit
-import Data.Text.IO as TIO (readFile)
-import qualified Data.Text as T
-
-main = do
-    cfg <- TIO.readFile "./conf.json"
-    s <- evalTemplateFile "template.tt" (JSONstring cfg)
-    case s of
-        (Right txt) -> putStr . T.unpack $ txt
-        (Left err) -> putStr ("ERROR! " ++ err)
-@
-
-=== conf.json
-@
-{
-  \"_CONFIG\":{
-    \"INCLUDE_PATH\":[\".\"]
-  },
-  \"users\":{
-    \"Foo\": 13,
-    \"Bar\": 3.14,
-    \"Baz\": \"bazzz\"
-  }
-}
-@
-
-=== template.tt
-@
-\<html>
-  \<body>
-    \<h1>Template Toolkit for Haskell\</h1>
-    \<h2>Count 1-10:\</h2>
-    [% FOREACH i = [1..10] -%]
-      [% i; (!loop.last) ? \', \' : \'.\' %]
-    [%- END %]
-    \<h2>Users hash:\</h2>
-    [% FOREACH user = users.pairs %]
-      \<p>[% user.key %]: [% user.value %]
-    [% END %]
-    \<h2>External template:\</h2>
-    [% PROCESS template2.tt words = [\'dog\',\'cat\',\'pig\'] %]
-  \</body>
-\</html>
-@
-
-=== template2.tt
-@
-\<p>[% words.sort.reverse.join(\'|\') %]
-@
--}
− Text/TemplateToolkitAST.hs
@@ -1,500 +0,0 @@-module Text.TemplateToolkitAST where
-
-import Text.Parsec
-import Text.Parsec.Text
-import Text.Parsec.Expr
-import Control.Applicative ((<*))
-import Control.Monad
-import Numeric
-import Data.List (foldl')
-import Data.Char (toUpper,toLower,isDigit)
-import qualified Data.Text as T
-import qualified Data.Map.Strict as Map
-import qualified Data.Sequence as Seq
-
---
------ types
---
-
-type Hash = Map.Map String Val
-type Array = Seq.Seq Val
-type Env = (Hash,Hash)
-type StmtId = (Int,Int,Int) -- (ParentID,SelfID,LastID)
-type LineN = Int
-type TName = String
-type Parser' = GenParser (TName,StmtId)
-
-data IString = IString T.Text | IVar Var deriving (Show)
-
-data Val = VString T.Text
-         | VIString [IString]
-         | VInt Integer
-         | VFloat Double
-         | VArray [Expr] -- array of Expressions (must be eval'ed)
-         | VArrayRange Expr Expr -- range from Expr1 to Expr2 (must be eval'ed)
-         | VArrayV Array -- array of Values (eval'ed and can be stored)
-         | VHash [(String,Expr)] -- hash of Expressions
-         | VHashV Hash  -- hash of Values
-         | VRef String -- reference to VArrayV or VHashV
-         | VCode Stmt
-         | Undef
-         deriving (Show)
-        
-data VarNode = VarKey String
-             | VarIndex Int
-             | VarMethod String [Expr]
-             | VarRef Var
-             deriving (Show)
-             
-data Var = Var [VarNode] deriving (Show)
-
-data Expr = EVal Val
-          | EVar Var
-          | EAssign Var Expr
-          | EUnOp UnOp Expr
-          | EBinOp BinOp Expr Expr
-          | ETerOp Expr Expr Expr
-          deriving (Show)
-        
-data UnOp = Pos | Neg | Not
-            deriving (Show)
-
-data BinOp = Add | Sub | Mul | Div | Mod | Con | Gt | Ge | Lt | Le | Eq | Ne | And | Or
-             deriving (Show)
-          
-data Else = Else Stmt | Elsif Expr Stmt (Maybe Else)
-    deriving (Show)
-
-data Stmt = Seq {sSeq :: [Stmt], sId :: StmtId, lineN :: LineN}
-          | SComment {sId :: StmtId, lineN :: LineN}
-          | SText {sText :: T.Text, sId :: StmtId, lineN :: LineN}
-          | SIf {sCond :: Expr, sBody :: Stmt, sElse :: (Maybe Else), sId :: StmtId, lineN :: LineN}
-          | SWhile {sCond :: Expr, sBody :: Stmt, sId :: StmtId, lineN :: LineN}
-          | SForeach {sForeachVar :: Var, sForeachArr :: Expr, sBody :: Stmt, sId :: StmtId, lineN :: LineN}
-          | SNext {sId :: StmtId, lineN :: LineN}
-          | SLast {sId :: StmtId, lineN :: LineN}
-          | SBlock {sBlockName :: String, sBody :: Stmt, sId :: StmtId, lineN :: LineN}
-          | SProcess {sProcessName :: IString, sProcessAssigns :: [Stmt], sId :: StmtId, lineN :: LineN}
-          | SWrapper {sWrapperName :: IString, sWrapperAssigns :: [Stmt], sBody :: Stmt, sId :: StmtId, lineN :: LineN}
-          | SAssign {sAssignVar :: Var, sAssignExpr :: Expr, sId :: StmtId, lineN :: LineN}
-          | SExpr {sExpr :: Expr, sId :: StmtId, lineN :: LineN}
-          | SFilter {sFilterName :: String, sFilterParams :: [Expr], sBody :: Stmt, sId :: StmtId, lineN :: LineN}
-          deriving (Show)
---
------ /types
---
-
---
------ parsers
---
-spaces1 :: Parser' ()
-spaces1 = skipMany1 space
-
-spacesAround :: Parser' a -> Parser' a
-spacesAround = between spaces spaces
-
-spaces1Around :: Parser' a -> Parser' a
-spaces1Around = between spaces1 spaces1
-
-parens :: Parser' a -> Parser' a
-parens = between (char '(' >> spaces) (spaces >> char ')')
-
-charCi :: Char -> Parser' Char
-charCi c = (char $ toUpper c) <|> (char $ toLower c)
-
-stringCi :: String -> Parser' String
-stringCi s = mapM charCi s
-
-escapedChar :: Parser' Char
-escapedChar = do
-    char '\\'
-    x <- oneOf "\\$\"'nt"
-    return $ case x of
-        '\\' -> x
-        '$' -> x
-        '"' -> x
-        '\'' -> x
-        'n' -> '\n'
-        't' -> '\t'
-
-parseString :: Parser' Val
-parseString = do
-    char '\''
-    x <- many $ noneOf "'\\" <|> escapedChar
-    char '\''
-    return $ VString (T.pack x)
-    
-parseInterpolatedVar :: Parser' Var
-parseInterpolatedVar = try $ (try $ between (string "${") (char '}') parseVar) <|> (char '$' >> parseVar)
-
-parseInterpolatedString :: Parser' Val
-parseInterpolatedString = do
-    char '"'
-    iStrings <- many $ ((many1 $ noneOf "\"$\\" <|> escapedChar) >>= return . IString . T.pack) <|> (parseInterpolatedVar >>= return . IVar)
-    char '"'
-    return $ VIString iStrings
-
-parseInt :: Parser' Val
-parseInt = do
-    num <- many1 digit
-    return $ VInt (read num)
-
-parseFloat :: Parser' Val
-parseFloat = try $ do
-    d1 <- many1 digit
-    char '.'
-    d2 <- many1 digit
-    return $ VFloat (fst $ (readFloat $ d1++"."++d2) !! 0)
-
-parseArray :: Parser' Val
-parseArray = do
-    char '[' >> spaces >> many (char ',' >> spaces)
-    list <- sepEndBy parseExpr (many $ spacesAround (char ','))
-    spaces >> char ']'
-    return $ VArray list
-    
-parseArrayRange :: Parser' Val
-parseArrayRange = try $ do
-    char '[' >> spaces
-    e1 <- parseExpr
-    spacesAround $ string ".."
-    e2 <- parseExpr
-    spaces >> char ']'
-    return $ VArrayRange e1 e2
-
-parseHashKeyVal :: Parser' (String,Expr)
-parseHashKeyVal = do
-    key <- (parseString >>= \(VString str) -> return $ T.unpack str) <|> many1 alphaNum
-    spaces >> string "=>" >> spaces
-    val <- parseExpr
-    return (key,val)
-    
-parseHash :: Parser' Val
-parseHash = do
-    char '{' >> spaces >> many (char ',' >> spaces)
-    list <- sepEndBy parseHashKeyVal (many $ spacesAround (char ','))
-    spaces >> char '}'
-    return $ VHash list
-
-parseVarKey :: Parser' VarNode
-parseVarKey = try $ do
-    let varletter' = ['A'..'Z']++['a'..'z']++['_']
-    c <- oneOf varletter'
-    cs <- many $ oneOf (varletter' ++ ['0'..'9'])
-    if reserved' (c:cs) then unexpected "reserved"
-    else return $ VarKey (c:cs)
-        where reserved' n = elem n ["BLOCK", "ELSE", "ELSIF", "END", "EXIT", "FILTER", "FOREACH", "IF", "LAST", "NEXT", "PROCESS", "WHILE", "WRAPPER"]
-    
-parseVarIndex :: Parser' VarNode
-parseVarIndex = liftM (VarIndex . read) $ many1 digit
-    
-parseVarMethod :: Parser' VarNode
-parseVarMethod = try $ do
-    VarKey methodName <- parseVarKey
-    exprs <- parens $ sepBy parseExpr (spacesAround (char ','))
-    return $ VarMethod methodName exprs
-    
-parseVarRef :: Parser' VarNode
-parseVarRef = do
-    char '$'
-    var <- parseVarKey
-    return $ VarRef (Var [var])
-
-parseVarRefDeep :: Parser' VarNode
-parseVarRefDeep = do
-    try $ string "${"
-    root <- parseVarKey
-    deeper <- many $ char '.' >> (parseVarKey <|> parseVarIndex)
-    char '}'
-    return $ VarRef (Var (root:deeper))
-    
-parseVar :: Parser' Var
-parseVar = do
-    root <- (parseVarMethod <|> parseVarKey)
-    deeper <- many $ char '.' >> (parseVarMethod <|> parseVarRefDeep <|> parseVarRef <|> parseVarKey <|> parseVarIndex)
-    return $ Var (root:deeper)
-
-parseMethodItem' :: Parser' VarNode
-parseMethodItem' = try $ do
-    string "item"
-    exprs <- parens $ sepBy parseExpr (spacesAround (char ','))
-    return $ VarMethod "item" exprs
-    
-parseStrictVar :: Parser' Var
-parseStrictVar = do
-    root <- parseVarKey
-    deeper <- many $ char '.' >> (parseMethodItem' <|> parseVarRefDeep <|> parseVarRef <|> parseVarKey <|> parseVarIndex)
-    return $ Var (root:deeper)
-    
-parseVal :: Parser' Expr
-parseVal = (liftM EVal $ parseHash <|> parseArrayRange <|> parseArray <|> parseString <|> parseInterpolatedString)
-            <|> (liftM EVar parseVar)
-            <|> (liftM EVal $ parseFloat <|> parseInt)
-
-parseTernary :: Parser' Expr
-parseTernary = try $ do
-    cond <- parseTerm
-    spacesAround $ char '?'
-    yes <- parseTerm
-    spacesAround $ char ':'
-    no <- parseTerm
-    return $ ETerOp cond yes no
-
-parseTerm :: Parser' Expr
-parseTerm = ((parens parseExpr) <|> parseVal) <* spaces
-    
-parseExpr = buildExpressionParser table (parseTernary <|> parseTerm)
-    where table =   [
-                    [Prefix $ try (char '-' >> notFollowedBy (char '%')) >> return (EUnOp Neg),
-                    Prefix $ char '+' >> return (EUnOp Pos),
-                    Prefix $ ((string "!" >> return ()) <|> try (stringCi "not" >> spaces1)) >> return (EUnOp Not)]
-                    ,[Infix (char '*' >> spaces >> return (EBinOp Mul)) AssocLeft,
-                    Infix (char '/' >> spaces >> return (EBinOp Div)) AssocLeft,
-                    Infix (try (char '%' >> notFollowedBy (char ']') >> spaces) >> return (EBinOp Mod)) AssocLeft,
-                    Infix (try (stringCi "mod" >> spaces1) >> return (EBinOp Mod)) AssocLeft]
-                    ,[Infix (char '+' >> spaces >> return (EBinOp Add)) AssocLeft,
-                    Infix (try (char '-' >> notFollowedBy (char '%') >> spaces) >> return (EBinOp Sub)) AssocLeft]
-                    ,[Infix (try (char '_' >> spaces1) >> return (EBinOp Con)) AssocLeft]
-                    ,[Infix (try (string ">=" >> spaces) >> return (EBinOp Ge)) AssocLeft,
-                    Infix (char '>' >> spaces >> return (EBinOp Gt)) AssocLeft,
-                    Infix (try (string "<=" >> spaces) >> return (EBinOp Le)) AssocLeft,
-                    Infix (char '<' >> spaces >> return (EBinOp Lt)) AssocLeft,
-                    Infix (try (string "==" >> spaces) >> return (EBinOp Eq)) AssocLeft,
-                    Infix (string "!=" >> spaces >> return (EBinOp Ne)) AssocLeft]
-                    ,[Infix (try (stringCi "gt" >> spaces1) >> return (EBinOp Gt)) AssocLeft,
-                    Infix (try (stringCi "ge" >> spaces1) >> return (EBinOp Ge)) AssocLeft,
-                    Infix (try (stringCi "lt" >> spaces1) >> return (EBinOp Lt)) AssocLeft,
-                    Infix (try (stringCi "le" >> spaces1) >> return (EBinOp Le)) AssocLeft,
-                    Infix (try (stringCi "eq" >> spaces1) >> return (EBinOp Eq)) AssocLeft,
-                    Infix (try (stringCi "ne" >> spaces1) >> return (EBinOp Ne)) AssocLeft]
-                    ,[Infix ((try (stringCi "and" >> spaces1) <|> (spacesAround (string "&&") >> return ())) >> return (EBinOp And)) AssocLeft,
-                    Infix ((try (stringCi "or" >> spaces1) <|> (try (spacesAround (string "||")) >> return ())) >> return (EBinOp Or)) AssocLeft]
-                    ]
-
-incrementSid :: Parser' StmtId
-incrementSid = do
-    (tName,(parent,prev,_)) <- getState
-    let newId = (parent,prev+1,prev+1)
-    putState (tName,newId)
-    return newId
-    
-getLineN :: Parser' LineN
-getLineN = liftM sourceLine getPosition
-
-parseComment :: Parser' Stmt
-parseComment = do
-    char '#'
-    lineN <- getLineN
-    manyTill anyChar (string "\n" <|> try (string "-%]" <|> string "%]"))
-    newId <- incrementSid
-    return $ SComment newId lineN
-    
-parseText :: Parser' Stmt
-parseText = do
-    spaces
-    try $ (string "-%]" <* spaces) <|> string "%]"
-    lineN <- getLineN
-    s <- manyTill anyChar (try ((try $ spaces >> string "[%-") <|> string "[%"))
-    spaces
-    newId <- incrementSid
-    return $ SText (T.pack s) newId lineN
-
-stmtEnd :: Parser' ()
-stmtEnd = do
-    spaces
-    eof <|> (char ';' >> spaces) <|> (lookAhead (string "-%]" <|> string "%]") >> return ())
-    
-parsePipeFilter :: Parser' (String,[Expr])
-parsePipeFilter = do
-    try $ spaces >> char '|' >> spaces1
-    (VarKey name) <- parseVarKey
-    exprs <- try (parens $ sepBy parseExpr (spacesAround (char ','))) <|> return []
-    return (name,exprs)
-    
-stmtEndFilter :: Parser' [(String,[Expr])]
-stmtEndFilter = do
-    spaces
-    fltrs <- many parsePipeFilter
-    eof <|> (char ';' >> spaces) <|> (lookAhead (string "-%]" <|> string "%]") >> return ())
-    return fltrs
-
-parseEnd :: Parser' ()
-parseEnd = (try $ string "END") >> stmtEnd
-
-parseMaybeElse :: Parser' (Maybe Else)
-parseMaybeElse = ((parseElsif <|> parseElse) >>= \els -> return $ Just els) <|> (parseEnd >> return Nothing)
-
-parseIf :: Parser' Stmt
-parseIf = do
-    try $ string "IF" >> spaces1
-    lineN <- getLineN
-    newId <- incrementSid
-    cond <- parseExpr
-    stmtEnd
-    body <- parseStmtSeq
-    maybeElse <- parseMaybeElse
-    return $ SIf cond body maybeElse newId lineN
-    
-parseElsif :: Parser' Else
-parseElsif = do
-    try $ string "ELSIF" >> spaces1
-    cond <- parseExpr
-    stmtEnd
-    body <- parseStmtSeq
-    maybeElse <- parseMaybeElse
-    return $ Elsif cond body maybeElse
-
-parseElse :: Parser' Else
-parseElse = do
-    try $ string "ELSE" >> stmtEnd
-    body <- parseStmtSeq
-    parseEnd
-    return $ Else body
-
-parseWhile :: Parser' Stmt
-parseWhile = do
-    try $ string "WHILE" >> spaces1
-    lineN <- getLineN
-    (tName,(parent,prev,_)) <- getState
-    putState (tName,(prev+1,prev+1,prev+1))
-    cond <- parseExpr
-    stmtEnd
-    body <- parseStmtSeq
-    parseEnd
-    return $ SWhile cond body (parent,prev+1,prev+1) lineN
-
-parseForeach :: Parser' Stmt
-parseForeach = do
-    try $ string "FOREACH" >> spaces1
-    lineN <- getLineN
-    (tName,(parent,prev,_)) <- getState
-    putState (tName,(prev+1,prev+1,prev+1))
-    (SAssign var expr _ _) <- parseAssign
-    stmtEnd
-    body <- parseStmtSeq
-    parseEnd
-    return $ SForeach var expr body (parent,prev+1,prev+1) lineN
-
-parseLast :: Parser' Stmt
-parseLast = do
-    try $ string "LAST" >> stmtEnd
-    lineN <- getLineN
-    newId <- incrementSid
-    return $ SLast newId lineN
-    
-parseNext :: Parser' Stmt
-parseNext = do
-    try $ string "NEXT" >> stmtEnd
-    lineN <- getLineN
-    newId <- incrementSid
-    return $ SNext newId lineN
-    
-parseBlock :: Parser' Stmt
-parseBlock = do
-    try $ string "BLOCK" >> spaces1
-    lineN <- getLineN
-    newId <- incrementSid
-    (VarKey name) <- parseVarKey
-    stmtEnd
-    body <- parseStmtSeq
-    parseEnd
-    return $ SBlock name body newId lineN
-
-parseTmplName :: Parser' IString
-parseTmplName = do
-    (liftM IVar parseInterpolatedVar) <|> do
-        (VarKey s) <- parseVarKey
-        ext <- option "" (many $ oneOf (['A'..'Z']++['a'..'z']++['_','.','-']))
-        return $ IString (T.pack (s ++ ext))
-
-parseProcess :: Parser' Stmt
-parseProcess = do
-    try $ string "PROCESS" >> spaces1
-    lineN <- getLineN
-    newId <- incrementSid
-    name <- parseTmplName
-    spaces
-    assigns <- many parseSimpleAssign
-    fltr <- stmtEndFilter
-    return $ case fltr of
-        [] -> SProcess name assigns newId lineN
-        fltrs -> foldr (\(fltrName,exprs) stmt -> SFilter fltrName exprs stmt newId lineN) (SProcess name assigns newId lineN) fltrs
-
-parseWrapper :: Parser' Stmt
-parseWrapper = do
-    try $ string "WRAPPER" >> spaces1
-    lineN <- getLineN
-    newId <- incrementSid
-    name <- parseTmplName
-    spaces
-    assigns <- many parseSimpleAssign
-    stmtEnd
-    body <- parseStmtSeq
-    parseEnd
-    return $ SWrapper name assigns body newId lineN
-        
-parseAssign :: Parser' Stmt
-parseAssign = do
-    lineN <- getLineN
-    var <- parseStrictVar
-    spacesAround $ char '='
-    expr <- parseExpr
-    newId <- incrementSid
-    return $ SAssign var expr newId lineN
-    
-parseSimpleAssign :: Parser' Stmt
-parseSimpleAssign = do
-    lineN <- getLineN
-    var <- parseStrictVar
-    spacesAround $ char '='
-    expr <- parseTernary <|> parseTerm
-    newId <- incrementSid
-    return $ SAssign var expr newId lineN
-
-parseStmtExpr :: Parser' Stmt
-parseStmtExpr = do
-    lineN <- getLineN
-    expr <- parseExpr
-    fltr <- stmtEndFilter
-    newId <- incrementSid
-    return $ case fltr of
-        [] -> SExpr expr newId lineN
-        fltrs -> foldl' (\stmt (fltrName,exprs) -> SFilter fltrName exprs stmt newId lineN) (SExpr expr newId lineN) fltrs
-        
-parseFilter :: Parser' Stmt
-parseFilter = do
-    try $ string "FILTER" >> spaces1
-    lineN <- getLineN
-    newId <- incrementSid
-    (VarKey name) <- parseVarKey
-    exprs <- try (parens $ sepBy parseExpr (spacesAround (char ','))) <|> return []
-    stmtEnd
-    body <- parseStmtSeq
-    parseEnd
-    return $ SFilter name exprs body newId lineN
-    
-parseStmtSeq :: Parser' Stmt
-parseStmtSeq = do
-    newId <- incrementSid
-    lineN <- getLineN
-    seq <- many1 (
-        parseComment <|>
-        parseText <|>
-        parseIf <|>
-        parseWhile <|>
-        parseForeach <|>
-        parseLast <|>
-        parseNext <|>
-        parseBlock <|>
-        parseProcess <|>
-        parseWrapper <|>
-        parseFilter <|>
-        (try $ parseAssign <* stmtEnd) <|>
-        parseStmtExpr
-        )
-    return $ Seq seq newId lineN
-    
-parseTemplateWithStmtId :: T.Text -> TName -> StmtId -> Either ParseError Stmt
-parseTemplateWithStmtId t tName sId = runParser parseStmtSeq (tName,sId) "" (T.concat [(T.pack "%]"), t, (T.pack "[%")])
-
-parseTemplate tName t = parseTemplateWithStmtId t tName (0,0,0)
− Text/TemplateToolkitLang.hs
@@ -1,453 +0,0 @@-{-|
-Module      : Text.TemplateToolkitLang
-Description : Template Toolkit language manual for Haskell
-Copyright   : (c) Dzianis Kabanau, 2017
-Maintainer  : kobargh@gmail.com
-
-Template Toolkit is a template processing system originally written in Perl by Andy Wardley.
-Below is the complete documentation of features implemented in Haskell version of the system. Original documentation is available <http://www.template-toolkit.org/docs/index.html here>, but be warned that not all features are realized in Haskell version.
-
--}
-
-module Text.TemplateToolkitLang (
-    -- * Tags
-    -- $tags
-    
-    -- * Variables
-    -- $variables
-    
-    -- * Operators
-    -- $operators
-    
-    -- * Directives
-    -- $directives
-    
-    -- * Virtual methods
-    -- $vmethods
-    
-    -- * Filters
-    -- $filters
-    
- ) where
-
-{-$tags
-
-Character sequences @__[% %]__@ are used to enclose directive tags within a template.
-
-@
- Hello __[% user.name %]__!
-@
-
-If directive tag starts by @__[%-__@ then all spaces before the tag are stripped. With @__-%]__@ all spaces after the tag are removed.
-
-Multiple directives may be enclosed within one tag. In this case they must be separated by @__;__@
-
-Comments are started by @__#__@
-
-@
- __[%__
-   __FOREACH__ __x__ = [0,15,100];
-     __x__;     /# Comments here/
-   __END__;
- __%]__
-@
--}
-
-{- $variables
-There are three types of variables implemented: SCALARS, ARRAYS and HASHES.
-
-Dots are used to traverse hashes and arrays: @__[% my_hash.key1 %]__@, @__[% my_array.0 %]__@.
-
-Arrays may be represented in range form: @__[% xs = [1..10] %]__@
-
-Actually arrays and hashes are kind-of Perl's arrayrefs and hashrefs.
-So, for example, if two variables refer to one hash, modifying one variable affects another too.
-Assignment to undefined hashes and arrays __autovivificates__ needed structures.
-
-@
- __[%__
-   /# scalar/
-   __int__ = 100;
-   __flt__ = 0.2;
-   __str__ = \"Hello \\\"guest\\\"\"; /# backslash is used to escape characters \" \' \\ $ and to type newlines \\n and tabs \\t./
-   
-   /# array/
-   __arr__ = [1,2,\"three\"];
-   __rng__ = [1..10];       /# [1,2,3,4,5,6,7,8,9,10]/
-   
-   /# hash/
-   __foo__ = {
-     k1 => 1,
-     k2 => \"value 2\",
-     k3 => [1,2,3],
-     k4 => {
-        subk1 => 10,
-        subk2 => 20,
-     }
-   };
-   
-   __bar__ = __foo.k4;__
-   __bar.subk1__ = \"this is ten\";
-   __foo.k4.subk1;__        /# \'this is ten\'/
-   
-   __bazarray.10.k1__ = 1; /# autovivification of array \'bazarray\' and hash \'bazarray.10\'/
-   
- __%]__
-@
-
-Variables may be __interpolated__ in double-quoted strings and in complex dotted variables (hashes or arrays).
-
-__$__ character is used to precede interpolated variable: @"__$var__"@. Complex variables may be enclosed in curly braces: @"__${myhash.k1.0}__"@. 
-
-Single-qouted strings do not interpolate variables.
-
-@
- __[%__
-   __i__ = 2;
-   __kbar__ = \'bar\';
-   __arr__ = [\'one\',\'two\',\'three\',\'four\',\'five\'];
-   __hsh__ = {foo => 10, bar => 20};
-   __arr.$i__; /# \'three\'/
-   \"Foo is __${hsh.foo}__.0\";         /# \'Foo is 10.0\'/
-   __hsh.$kbar__; /# 20/
- __%]__
-@
--}
-
-{-$operators
-=== Arithmetic operators
-
-@
- __[%__
-   15 + 3.14;    /# 18.14/
-   12 - 10;      /# 2/
-   2 * 2;        /# 4/
-   10 \/ 4;       /# 2.5/
-
-   5 % 2;        /# 1       modulus (remainder) of division/
-                 /#         \'mod\' - synonym of \'%\'/
-   13 mod 5;     /# 3/
-
-   +7;           /# 7       unary positive/
-   -7;           /# -7      unary negative/
- __%]__
-@
-
-=== Logical operators
-__0__ and empty strings are /false/ values. Everything else is /true/. Internal representation of /true\/false/ is __1__\/__0__ respectively.
-
-If both operands of comparison are strings, then they are compared as strings. Otherwise string operand is parsed as number.
-
-@
- __[%__
-   100 __>__ 99;            /# 1        true/
-   100 __>=__ 101;          /# 0        false/
-   100 __<__ 100;           /# 0        false/
-   100 __<=__ 100;          /# 1        true/
-   100 __==__ 100;          /# 1        true/
-   100 __!=__ 100;          /# 0        false/
-   __!__0;                  /# 1        unary negation/
-   
-   \"100\" > 2;           /# 1        true - numeric comparison/
-   \"100\" > \"2\";         /# 0        false - string comparison/
-   
-   0 __||__ 100;            /# 100/
-                        /# \'or\' - synonym of \'||\'/
-   30 __or__ \'\';            /# 30/
-   
-   \'fox\' __&&__ \'dog\';      /# \'dog\'/
-                        /# \'and\' - synonym of \'&&\'/
-   \'fox\' __and__ \"\";        /# 0        evaluates to false value/
-   
-   /# operations group with ( )/
-   (\"string\" && 0) __?__ \"snow\" __:__ \"rain\";      /# \'rain\'/
-   /# cond ? val_true : val_false          ternary condition operator/
-   /# complex operands of ternary condition operator must be enclosed in parentheses:/
-   (!loop.last) ? \'not last\' : \'last\' /# \'not last\' or \'last\'/
-   !loop.last ? \'not last\' : \'last\' /# always evals to 0 - same as !(loop.last ? \'not last\' : \'last\')/
- __%]__
-@
-
-=== String concatenation
-Underscore @__\___@ separated by spaces is used to concatenate strings.
-
-@
- __[%__
-   __name__ = \"Zork\";
-   \"Hello,\" _ __name__;        /# \'Hello, Zork\'/
- __%]__
-@
--}
-
-{-$directives
-=== Conditionals
-
-__IF__ ... [ELSIF...] [ELSE...] __END__
-
-@
-__[% IF user.name__ == \'root\' __%]__
-
-Hello, Admin!
-
-__[% ELSIF user.status__ > 1 __%]__
-
-Welcome, master __[% user.name %]__!
-
-__[% ELSE %]__
-
-Hi, __[% user.name %]__!
-
-__[% END %]__
-@
-
-=== Loops
-
-__FOREACH__ x = ... __END__
-
-Special variable @__loop__@ is used to save information about foreach loop and current iteration.
-
-@
-__[%__ __FOREACH__ __i__ = [10..15] __%]__
-
- I is __[% i %]__
-
- __[%__
-   __IF__ __i__ == 12;
-     __loop.index;__ /# 2       current index/
-     __loop.count;__ /# 3       iteration count - index+1/
-     __loop.first;__ /# 10      first loop element/
-     __loop.last;__  /# 15      last loop element/
-     __loop.prev;__  /# 11      previous loop element/
-     __loop.next;__  /# 13      next loop element/
-     __loop.odd;__   /# 1       true if iteration count is odd/
-     __loop.even;__  /# 0       true if iteration count is even/
-   __END;__
- __%]__
-
-__[% END %]__
-@
-
-__WHILE__ ... __END__
-
-@
-__[%__
-  x = 1;
-  __WHILE__ x <= 20;
-
-    "| X$x ";
-    x = x + 1; __IF__ x > 3; __LAST__; __END__;
-
-    __FOREACH__ y = [1..10];
-      __IF__ y > 2 && y < 9; __NEXT__; __END__;
-      "y$y ";
-    __END__;
-    
-  __END__;
-  /# Output: | X1 y1 y2 y9 y10 | X2 y1 y2 y9 y10 | X3 y1 y2 y9 y10/
-__%]__
-@
-
-__LAST__ is used to immediately break current loop. __NEXT__ - to skip to the next iteration.
-
-=== Processing template files and blocks
-
-__PROCESS__ /blockname_or_filename/ [var1 = ... var2 = ... varN = ...]
-
-__BLOCK__ /blockname/ ... __END__
-
-@
-\<html>
-  __[%__ __PROCESS__ header.tt __title__ = \'Homepage\' __descr__ = \'Homepage description\' __keyw__ = \'homepage,home,firstpage\' __%]__
-
-  \<p>Hello, __[% user.name %]__!
-  
-  __[% BLOCK__ foobarbazzer __%]__
-    \<p>Foo__[% word %]__barbaz
-  __[% END %]__
-  
-  __[%__
-    __FOREACH w__ = [\'fox\', \'dog\', \'cat\'];
-      __PROCESS__ foobarbazzer __word__ = __w__; /# \<p>Foofoxbarbaz \<p>Foodogbarbaz \<p>Foocatbarbaz/
-    __END__;
-  __%]__
-  
-  __[%__ __PROCESS__ footer.tt __%]__
-\</html>
-@
-
-Variable may be passed to __PROCESS__ or __WRAPPER__ directive as a name using __$__: @__[% PROCESS $template %]__@
-
-=== Wrapper
-
-__WRAPPER__ /wrapper_block_or_filename/ [var1 = ... var2 = ... varN = ...] ... __END__
-
-The @__WRAPPER__@ block evaluates its content and passes the result in the variable @__content__@ to /wrapper_block_or_filename/. This is very useful for web templates with the same skeleton.
-
-@
-/FILE index.tt/
-__[% WRAPPER html_wrap.tt title = \'Barbaz homepage!\' %]__
-\<p>Welcome to Barbaz home!
-__[% END %]__
-
-
-/FILE html_wrap.tt/
-\<html>
-\<head>
- \<meta http-equiv=\"content-type\" content=\"text/html; charset=utf-8\">
- \<title>__[% title %]__\</title>
-\</head>
-\<body>
-__[% content %]__
-\</body>
-\</html>
-@
-
--}
-
-{-$vmethods
-
-Virtual methods are applied to the variables to get new values or to modify array or hash variable.
-
-@__[% my_array.size %]__@ - Get the number of elements in @__my_array__@.
-
-@__[% karr = my_hash.keys %]__@ - Create array @__karr__@. The elements are the keys of @__my_hash__@.
-
-@__[% my_array.push(\"Baz\") %]__@ - Modify array @__my_array__@ appending scalar @__\"Baz\"__@ to the end of the array.
-
-Methods that use regular expressions are based on __pcre__ patterns, e.g. @__[% bar.match(\'(?i)^baz\\\\d$\') %]__@.
-
-Below is the list of implemented virtual methods. You can also take a look at the original documentation: <http://www.template-toolkit.org/docs/manual/VMethods.html Template Toolkit VMethods>.
-
-===Scalar vmethods
-[@.collapse@] Returns the string with trailing and leading spaces removed and internal multiple spaces converted to single space.
-
-[@.defined@] Returns true if the value is defined.
-
-[@.lcfirst@] Returns the string with first character converted to lower case.
-
-[@.length@] Returns the length of the string.
-
-[@.lower@] Returns the string converted to lower case.
-
-[@.match(/pattern/, /global/)@] Returns the array of captured substrings from regexing target string with @/pattern/@. If pattern-match fails, returns @/false/@. Optional @/global/@ is used for global pattern-match to return all captured substrings.
-
-[@.replace(/old/, /new/)@] Returns the string with all occurrences of @/old/@ replaced by @/new/@.
-
-[@.split(/pattern/)@] Returns the array produced by splitting the string by @/pattern/@. 
-
-[@.trim@] Returns the string with leading and trailing spaces removed.
-
-[@.ucfirst@] Returns the string with first character converted to upper case.
-
-[@.upper@] Returns the string converted to upper case.
-
-===Array vmethods
-
-[@.first@] Returns the first element of the array.
-
-[@.grep(/pattern/)@] Returns the array of elements that match @/pattern/@.
-
-[@.import(/arr1/, /arr2/, /arrN/)@] Imports to the target array elements of all passed arrays. Target array is modified.
-
-[@.join(/separator/)@] Returns the string created by joining all elements of the array separated by @/separator/@.
-
-[@.last@] Returns the last element of the array.
-
-[@.nsort@] Returns the array sorted numerically.
-
-[@.pop@] Removes the last element from the array. Target array is modified.
-
-[@.push(/el1/, /el2/, /elN/)@] Appends element(s) to the end of the array. Target array is modified.
-
-[@.reverse@] Returns reversed version of the array.
-
-[@.shift@] Removes the first element from the array. Target array is modified.
-
-[@.size@] Returns the number of elements in the array.
-
-[@.slice(/from/, /to/)@] Returns the array of elements idexing in the range @/from/../to/@ of the original array. If @/to/@ is omitted, then to the last element.
-
-[@.sort@] Returns the array sorted alphabetically.
-
-[@.splice(/offset/,/length/,/insertarray/)@] Powerful array-modification method that behaves just like <https://perldoc.perl.org/functions/splice.html Perl's splice>. Removes from the array @/length/@ elements starting from @/offset/@ index, replacing them with @/insertarray/@. If @/insertarray/@ is omitted - just removes elements. If @/length/@ is omitted removes everything up to the end of the array. @/length/@ may be @0@ - no elements are removed. @/offset/@ may be negative - counts index from the end of the array, e.g. @-2@ - penultimate element's index. Target array is modified.
-
-[@.unique@] Returns the array of only unique elements.
-
-[@.unshift(/el1/, /el2/, /elN/)@] Prepends element(s) to the beginning of the array. Target array is modified.
-
-===Hash vmethods
-
-[@.delete(/key1/, /key2/, /keyN/)@] Deletes items identified by the keys from the hash. Target hash is modified.
-
-[@.each@] Returns keys and values of the hash in one array - @[key1, val1, key2, val2, ... keyN, valN]@.
-
-[@.keys@] Returns the array of the hash keys.
-
-[@.import(/hash1/, /hash2/, /hashN/)@] Imports to the target hash items of all passed hashes. Existing items get new values, new items are added. Target hash is modified.
-
-[@.item(/key/)@] Returns the value of hash item identified by /key/. This is useful to get values with keys that contain any characters. E.g. @__[% hash.item('complex $ key.') %]__@ or @__[% hash.item('12') %]__@.
-
-[@.pairs@] Returns the array of key-value pairs of the hash. The pairs are hashes each with two keys: @__key__@ and @__value__@.
-
-[@.size@] Returns the number of items in the hash.
-
-[@.values@] Returns the array of the hash values.
-
-Sometimes a hash may have items whith keys that conflict with the above method names. In this situation parentheses may be used to force method call.
-
-@
-__[%__
-  __my_car__ = {
-    \'keys\' => \'lost\',
-    \'mileage\' => 150000,
-  };
-  __my_car.keys__; /# \'lost\'/
-  __my_car.keys()__; /# [\'keys\', \'mileage\']/
-__%]__
-@
-
--}
-
-{-$filters
-
-Filters transform text of a block. Filter may be applied as a directive:
-
-__FILTER__ /filter_name/ ... __END__
-
-Or in piped form (multiple filters may be chained):
-
-/expr/ __|__ /filter_name [| /filter_name]/
-
-@
-__[% FILTER collapse %]__
-__[% PROCESS body.tt %]__
-__[% END %]__
-
-The same:
-
-__[% PROCESS body.tt | collapse %]__
-@
-
-Below is the list of implemented filters.
-
-[@collapse@] Removes leading and trailing spaces. Internal multiple spaces are converted to single space.
-
-[@html@] Replaces all @__\<__@, @__>__@, @__&__@, @__\"__@ with @__&lt;__@, @__&gt;__@, @__&amp;__@, @__&quot;__@ respectively.
-
-[@lcfirst@] Converts the first character to lower case.
-
-[@lcfirst@] Converts all characters to lower case.
-
-[@null@] Discards the text of block
-
-[@replace(/old/, /new/)@] Replaces all occurrences of @/old/@ with @/new/@.
-
-[@trim@] Removes leading and trailing spaces.
-
-[@ucfirst@] Converts the first character to upper case.
-
-[@upper@] Converts all characters to upper case.
-
-[@uri@] URI-encodes text.
-
--}
+ example/Main.hs view
@@ -0,0 +1,11 @@+import Text.TemplateToolkit
+import qualified Data.Text.IO as TIO (readFile)
+import qualified Data.Text as T
+
+main = do
+    cfg <- TIO.readFile "./conf.json"
+    s <- evalTemplateFile "template.tt" (JSONstring cfg)
+    case s of
+        (Right txt) -> putStr . T.unpack $ txt
+        (Left err) -> putStr ("ERROR! " ++ err)
+        
− example/template-toolkit-example.hs
@@ -1,11 +0,0 @@-import Text.TemplateToolkit
-import qualified Data.Text.IO as TIO (readFile)
-import qualified Data.Text as T
-
-main = do
-    cfg <- TIO.readFile "./conf.json"
-    s <- evalTemplateFile "template.tt" (JSONstring cfg)
-    case s of
-        (Right txt) -> putStr . T.unpack $ txt
-        (Left err) -> putStr ("ERROR! " ++ err)
-        
+ src/Text/TemplateToolkit.hs view
@@ -0,0 +1,1048 @@+{-|
+Module      : Text.TemplateToolkit
+Description : Template Toolkit implementation for Haskell
+Copyright   : (c) Dzianis Kabanau, 2018
+Maintainer  : kobargh@gmail.com
+
+This is a Haskell implementation of <http://www.template-toolkit.org Template Toolkit> - the popular Perl template processing system.
+
+-}
+
+module Text.TemplateToolkit (
+    -- * Documentation
+    TName
+    ,TErr
+    ,TConfig(..)
+    ,evalTemplateFile
+    -- $conf
+
+    -- * Example
+    -- $example
+ ) where
+
+import Text.TemplateToolkitAST
+import Data.List
+import Data.Foldable (toList)
+import Control.Applicative ((<*))
+import Control.Monad.Reader
+import Control.Monad.Except
+import System.Directory
+import qualified Data.HashTable.IO as HTIO
+import qualified Data.Map.Strict as Map
+import qualified Data.Sequence as Seq
+import qualified Data.HashMap.Lazy as HashMap (toList)
+
+import Data.Char (toUpper,toLower,isDigit)
+import qualified Data.Text as T
+import qualified Data.Text.IO as TIO (readFile)
+import qualified Data.Text.Encoding as TE (decodeUtf8)
+import qualified Data.Text.Lazy as TL (fromStrict)
+import qualified Data.Text.Lazy.Encoding as TLE (encodeUtf8)
+import qualified Data.ByteString.Char8 as BS
+
+import Data.Scientific (floatingOrInteger)
+import Data.Time.Calendar (fromGregorian)
+import Data.Time.Clock (UTCTime(..),secondsToDiffTime)
+import qualified Data.Aeson as Aeson
+import qualified Data.Binary as Bin
+import Text.Regex.PCRE
+import qualified Text.Regex.PCRE.ByteString.Utils as PCRE
+import qualified Network.URI.Encode as URI (encodeText)
+
+import Debug.Trace
+
+type EnvHash = HTIO.BasicHashTable String Val
+type Env = (EnvHash, EnvHash)
+type TErr = String
+
+type EvalWithExcept = ExceptT TErr (ReaderT Env IO)
+
+instance Eq Val where
+    (==) (VFloat x) (VFloat y) = x == y
+    (==) (VFloat x) (VInt y) = x == (fromInteger y)
+    (==) (VInt x) (VFloat y) = (fromInteger x) == y
+    (==) (VInt x) (VInt y) = x == y
+    (==) x@(VFloat _) y = x == (toNum y)
+    (==) x y@(VFloat _) = (toNum x) == y
+    (==) x@(VInt _) y = x == (toNum y)
+    (==) x y@(VInt _) = (toNum x) == y
+    (==) (VString a) (VString b) = a == b
+
+instance Ord Val where
+    compare (VFloat x) (VFloat y) = compare x y
+    compare (VFloat x) (VInt y) = compare x (fromInteger y)
+    compare (VInt x) (VFloat y) = compare (fromInteger x) y
+    compare (VInt x) (VInt y) = compare x y
+    compare (VString x) (VString y) = compare x y
+    compare x@(VString _) y@(VFloat _) = compare (toNum x) y
+    compare x@(VString _) y@(VInt _) = compare (toNum x) y
+    compare x@(VFloat _) y@(VString _) = compare x (toNum y)
+    compare x@(VInt _) y@(VString _) = compare x (toNum y)
+    
+instance Num Val where
+    (+) (VInt x) (VInt y) = VInt (x + y)
+    (+) (VInt x) (VFloat y) = VFloat (fromInteger x + y)
+    (+) (VFloat x) (VFloat y) = VFloat (x + y)
+    (+) (VFloat x) (VInt y) = VFloat (x + fromInteger y)
+    (+) x y = (toNum x) + (toNum y)
+    (*) (VInt x) (VInt y) = VInt (x * y)
+    (*) (VInt x) (VFloat y) = VFloat (fromInteger x * y)
+    (*) (VFloat x) (VFloat y) = VFloat (x * y)
+    (*) (VFloat x) (VInt y) = VFloat (x * fromInteger y)
+    (*) x y = (toNum x) * (toNum y)
+    abs (VInt x) = VInt (abs x)
+    abs (VFloat x) = VFloat (abs x)
+    abs x = abs (toNum x)
+    signum (VInt x) = VInt (signum x)
+    signum (VFloat x) = VFloat (signum x)
+    signum x = signum (toNum x)
+    fromInteger = VInt
+    negate (VInt x) = VInt (negate x)
+    negate (VFloat x) = VFloat (negate x)
+    negate x = negate (toNum x)
+    
+instance Fractional Val where
+    fromRational = VFloat . fromRational
+    (/) (VFloat x) (VFloat y) = VFloat (x / y)
+    (/) (VFloat x) (VInt y) = VFloat (x / fromInteger y)
+    (/) (VInt x) (VFloat y) = VFloat (fromInteger x / y)
+    (/) (VInt x) (VInt y) = VFloat (fromInteger x / fromInteger y)
+    (/) x y = (toNum x) / (toNum y)
+
+    
+fromAeson :: Aeson.Value -> Expr
+fromAeson Aeson.Null = EVal Undef
+fromAeson (Aeson.Bool True) = EVal $ VInt 1
+fromAeson (Aeson.Bool False) = EVal $ VInt 0
+fromAeson (Aeson.Number x) = case floatingOrInteger x of
+    (Left f) -> EVal . VFloat . realToFrac $ f
+    (Right i) -> EVal . VInt . fromInteger $ i
+fromAeson (Aeson.String s) = EVal $ VString s
+fromAeson (Aeson.Array a) = EVal . VArray . (map fromAeson) . toList $ a
+fromAeson (Aeson.Object o) = EVal . VHash . (map (\(k,v) -> (T.unpack k,fromAeson v))) . HashMap.toList $ o
+    
+refTableInsert :: Val -> EvalWithExcept String
+refTableInsert v = do
+    (_,reft) <- lift ask
+    size <- liftIO $ fmap length (HTIO.toList reft)
+    let refId = reftype ++ "ref#" ++ show (size + 1)
+            where reftype = case v of
+                    VHashV _ -> "hash"
+                    VArrayV _ -> "array"
+                    _ -> ""
+    liftIO $ HTIO.insert reft refId v
+    return refId
+    
+refTableReplace :: String -> Val -> EvalWithExcept ()
+refTableReplace r v = do
+    (_,reft) <- lift ask
+    liftIO $ HTIO.insert reft r v
+
+getFromHTIO :: String -> EnvHash -> EvalWithExcept Val
+getFromHTIO k h = liftIO $ do
+    v <- HTIO.lookup h k
+    case v of
+        Nothing -> return Undef
+        Just v' -> return v'
+
+getFromVars :: String -> EvalWithExcept Val
+getFromVars k = do
+    (vars,_) <- lift ask
+    getFromHTIO k vars
+
+getFromRefTable :: String -> EvalWithExcept Val
+getFromRefTable r = do
+    (_,reft) <- lift ask
+    getFromHTIO r reft
+
+getFromRefTableDef :: Val -> String -> EvalWithExcept Val
+getFromRefTableDef def r = do
+    v <- getFromRefTable r
+    case v of
+        Undef -> return def
+        _ -> return v
+
+getVal :: (String,Val) -> VarNode -> EvalWithExcept (String,Val)
+getVal (_,VRef r') k = do
+    v <- getFromRefTable r'
+    getVal (r',v) k
+getVal (r,VHashV h) (VarKey k) = do
+    case Map.lookup k h of
+        Nothing -> case lookup k vmethods of
+            Nothing -> return (r,Undef)
+            Just m -> m (VHashV h) [] r
+        Just v -> return (r,v)
+getVal (r,VArrayV a) (VarIndex i) = return $ if Seq.length a <= (fromIntegral i) then (r,Undef) else (r,Seq.index a i)
+getVal (r,v) (VarMethod met pars) = case lookup met vmethods of
+    -- Nothing -> return (r,VString . T.pack $ "Invalid method '" ++ met ++ "'")
+    Nothing -> throwError ("Invalid method '" ++ met ++ "'")
+    Just m -> m v pars r
+getVal (r,v) (VarKey k) = do
+    case lookup k vmethods of
+        Nothing -> return (r,Undef)
+        Just m -> m v [] r
+getVal (r,_) _ = return (r,Undef)
+    
+getVar :: Var -> EvalWithExcept (String,Val)
+getVar (Var ((VarKey k0):vs)) = do
+    val <- getFromVars k0
+    getVar' ("",val) vs
+    where getVar' (r,val) (v':vs') = do
+                k' <- getNode v'
+                (r',v') <- getVal (r,val) k'
+                getVar' (r',v') vs'
+          getVar' (r,val) [] = return (r,val)
+          getNode (VarRef r) = do
+                val <- getVar r
+                case val of
+                    (_,VInt i) -> return $ VarIndex (fromIntegral i)
+                    (_,v) -> return $ VarKey (toString v)
+          getNode n = return n
+
+getVarVal :: Var -> EvalWithExcept Val
+getVarVal var = do
+    (_,val) <- getVar var
+    case val of
+        (VRef r) -> getFromRefTable r
+        _ -> return val
+          
+toNum :: Val -> Val
+toNum (VString s) = case reads (T.unpack s) :: [(Integer,String)] of
+    [] -> case reads (T.unpack s) :: [(Double,String)] of
+        [] -> VInt 0
+        (s',_):_ -> VFloat s'
+    (s',_):_ -> VInt s'
+toNum v@(VInt _) = v
+toNum v@(VFloat _) = v
+toNum _  = VInt 0
+
+toInt :: Val -> Int
+toInt (VFloat v) = fromIntegral . truncate $ v
+toInt (VInt v) = fromIntegral v
+toInt v = toInt . toNum $ v
+
+toString :: Val -> String
+toString (VString v) = T.unpack v
+toString (VInt v) = show v
+toString (VFloat v) = show v
+toString (VRef v) = v
+toString _ = ""
+
+toText :: Val -> T.Text
+toText (VString v) = v
+toText v = T.pack . toString $ v
+
+not' :: Val -> Val
+not' (VString s) = if (T.unpack s) == "" then VInt 1 else VInt 0
+not' (VInt 0) = VInt 1
+not' (VFloat 0.0) = VInt 1
+not' (Undef) = VInt 1
+not' _ = VInt 0
+
+evalUnOp :: UnOp -> Expr -> EvalWithExcept Val
+evalUnOp Pos e = liftM toNum (evalExpr e)
+evalUnOp Neg e = liftM (negate . toNum) (evalExpr e)
+evalUnOp Not e = liftM not' (evalExpr e)
+
+isValTrue :: Val -> Bool
+isValTrue v = not' v == VInt 0
+
+evalBinOp' :: (Val -> Val -> Val) -> Expr -> Expr -> EvalWithExcept Val
+evalBinOp' f e1 e2 = liftM2 f (evalExpr e1) (evalExpr e2)
+evalBinBoolOp' :: (Val -> Val -> Bool) -> Expr -> Expr -> EvalWithExcept Val
+evalBinBoolOp' f e1 e2 = do
+    x' <- evalExpr e1
+    y' <- evalExpr e2
+    case f x' y' of
+        True -> return $ VInt 1
+        False -> return $ VInt 0
+evalBinOp :: BinOp -> Expr -> Expr -> EvalWithExcept Val
+evalBinOp Add = evalBinOp' (+)
+evalBinOp Sub = evalBinOp' (-)
+evalBinOp Mul = evalBinOp' (*)
+evalBinOp Div = div'
+    where div' x y = do
+            x' <- evalExpr x
+            y' <- evalExpr y
+            if (toInt y') == 0 then throwError "Division by zero" else return (x' / y')
+evalBinOp Mod = mod'
+    where mod' x y = do
+                x' <- evalExpr x
+                y' <- evalExpr y
+                mod'' x' y'
+          mod'' :: Val -> Val -> EvalWithExcept Val
+          mod'' (VInt x) (VInt y) = if y /= 0 then (return $ VInt (x `mod` y)) else throwError "Division by zero"
+          mod'' _ _ = throwError "Not integer in 'mod' operation"
+evalBinOp Con = con'
+    where con' x y = do
+            x' <- evalExpr x
+            y' <- evalExpr y
+            return $ VString (toText x' `T.append` toText y')
+evalBinOp Gt = evalBinBoolOp' (>)
+evalBinOp Ge = evalBinBoolOp' (>=)
+evalBinOp Lt = evalBinBoolOp' (<)
+evalBinOp Le = evalBinBoolOp' (<=)
+evalBinOp Eq = evalBinBoolOp' (==)
+evalBinOp Ne = evalBinBoolOp' (/=)
+evalBinOp And = and'
+    where and' e1 e2 = do
+            v1' <- evalExpr e1
+            if isValTrue v1'
+            then do
+                v2' <- evalExpr e2
+                if isValTrue v2'
+                then return v2'
+                else return $ VInt 0
+            else return $ VInt 0
+evalBinOp Or = or'
+    where or' e1 e2 = do
+            v1' <- evalExpr e1
+            if isValTrue v1'
+            then return v1'
+            else do
+                v2' <- evalExpr e2
+                if isValTrue v2'
+                then return v2'
+                else return $ VInt 0
+
+evalExpr :: Expr -> EvalWithExcept Val
+evalExpr (EVal (VArray xs)) = do
+    xs' <- mapM evalExprWithRef xs
+    return $ VArrayV $ Seq.fromList xs'
+evalExpr (EVal (VArrayRange e1 e2)) = do
+    from' <- evalExpr e1
+    to' <- evalExpr e2
+    return $ VArrayV . Seq.fromList . (map (VInt . fromIntegral)) $ [(toInt from')..(toInt to')]
+evalExpr (EVal a@(VArrayV _)) = do
+    return a
+evalExpr (EVal (VHash xs)) = do
+    let (ks',es') = unzip xs
+    vs' <- mapM evalExprWithRef es'
+    return $ VHashV (Map.fromList (zip ks' vs'))
+evalExpr (EVal (VIString vs)) = do
+    let getVIString (IString s) = return s
+        getVIString (IVar v) = liftM (toText . snd) (getVar v)
+    vs' <- mapM getVIString vs
+    return $ VString (T.concat vs')
+    
+evalExpr (EVal v) = return v
+evalExpr (EVar var) = do
+    (_,val) <- getVar var
+    return val
+evalExpr (EUnOp op e) = evalUnOp op e
+evalExpr (EBinOp op e1 e2) = evalBinOp op e1 e2
+evalExpr (ETerOp e1 e2 e3) = do
+    v1' <- evalExpr e1
+    if isValTrue v1'
+    then evalExpr e2
+    else evalExpr e3
+
+evalExprWithRef :: Expr -> EvalWithExcept Val
+evalExprWithRef e = do
+    v <- evalExpr e
+    case v of
+        a@(VArrayV _) -> do
+            id <- refTableInsert a
+            return $ VRef id
+        h@(VHashV _) -> do
+            id <- refTableInsert h
+            return $ VRef id
+        x -> return x
+
+--
+----- statement evaluators
+--
+
+fst3 :: (a,b,c) -> a
+fst3 (x,_,_) = x
+
+snd3 :: (a,b,c) -> b
+snd3 (_,x,_) = x
+
+trd3 :: (a,b,c) -> c
+trd3 (_,_,x) = x
+
+evalOrSkipStmt :: Stmt -> EvalWithExcept T.Text
+evalOrSkipStmt stmt = do
+    (VHashV loops) <- getFromRefTableDef (VHashV Map.empty) "#loopcontrols"
+    let (VInt breakCode) = Map.findWithDefault (VInt 0) (show . fst3 . sId $ stmt) loops
+    if breakCode == 0
+    then evalStmt stmt
+    else return T.empty
+
+evalStmt :: Stmt -> EvalWithExcept T.Text
+
+evalStmt (Seq ss stmtId _) = do
+    ss' <- mapM evalOrSkipStmt ss
+    return $ T.concat ss'
+
+evalStmt (SComment _ _) = return T.empty
+    
+evalStmt (SText s stmtId _) = return s
+
+evalStmt (SExpr e stmtId _) = do
+    val <- evalExpr e
+    return $ toText val
+    
+evalStmt assign@(SAssign (Var varPath@((VarKey k0):vs)) e stmtId lineN) = do
+    val <- evalExprWithRef e
+    if null vs
+    then do
+        (vars,_) <- lift ask
+        liftIO $ HTIO.insert vars k0 val
+        return T.empty
+    else do
+        var <- getVar (Var $ init varPath)
+        updateRefTable var (last varPath) val
+            where updateRefTable :: (String,Val) -> VarNode -> Val -> EvalWithExcept T.Text
+                  updateRefTable (r,VHashV h) (VarKey k) val = do
+                        refTableReplace r (VHashV $ Map.insert k val h)
+                        return T.empty
+                  updateRefTable (r,VHashV h) (VarMethod "item" (e':_)) val = do
+                        k' <- evalExpr e'
+                        refTableReplace r (VHashV $ Map.insert (toString k') val h)
+                        return T.empty
+                  updateRefTable (r,VArrayV a) (VarIndex i) val = do
+                        refTableReplace r (VArrayV $ updateArr' i val a)
+                        return T.empty
+                        where updateArr' i val a = if (Seq.length a > i) then (Seq.update i val a)
+                                                   else (a Seq.>< (Seq.replicate (i - Seq.length a) (Undef))) Seq.|> val
+                  updateRefTable (_,VRef r) vark val = do
+                        v <- getFromRefTable r
+                        updateRefTable (r,v) vark val
+                        return T.empty
+                  -- auto-vivification
+                  updateRefTable _ k@(VarKey _) _ = do
+                        evalStmt $ SAssign (Var $ init varPath) (EVal (VHashV Map.empty)) stmtId lineN
+                        evalStmt assign
+                  updateRefTable _ k@(VarMethod "item" _) _ = do
+                        evalStmt $ SAssign (Var $ init varPath) (EVal (VHashV Map.empty)) stmtId lineN
+                        evalStmt assign
+                  updateRefTable _ k@(VarIndex _) _ = do
+                        evalStmt $ SAssign (Var $ init varPath) (EVal (VArrayV Seq.empty)) stmtId lineN
+                        evalStmt assign
+                  -- otherwise
+                  updateRefTable v k val = throwError (show lineN ++ ": Variable assign error:\nvariable path: " ++ (show varPath) ++ "\nvalue: " ++ (show val) ++ "\n")
+    
+evalStmt (SIf cond body maybeElse stmtId _) = do
+    v' <- evalExpr cond
+    if isValTrue v'
+    then evalOrSkipStmt body
+    else evalElse' maybeElse
+        where evalElse' Nothing = return T.empty
+              evalElse' (Just (Elsif cond' body' maybeElse')) = do
+                    v'' <- evalExpr cond'
+                    if isValTrue v''
+                    then evalOrSkipStmt body'
+                    else evalElse' maybeElse'
+              evalElse' (Just (Else body'')) = evalOrSkipStmt body''
+          
+evalStmt whileStmt@(SWhile cond body stmtId _) = do
+    isSkip <- loopStart (snd3 . sId $ whileStmt)
+    val <- evalExpr cond
+    if isValTrue val && (not isSkip)
+    then do
+        v' <- evalOrSkipStmt body
+        v'' <- evalOrSkipStmt whileStmt
+        return $ T.append v' v''
+    else return T.empty
+    
+evalStmt foreachStmt@(SForeach var expr body stmtId lineN) = do
+    isSkip <- loopStart (snd3 . sId $ foreachStmt)
+    val <- evalExpr expr
+    foreach' val isSkip <* loopEnd (sId foreachStmt)
+        where foreach' :: Val -> Bool -> EvalWithExcept T.Text
+              foreach' (VRef r') False = do
+                    v <- getFromRefTable r'
+                    foreach' v False
+              foreach' (VArrayV a') False = case toList a' of
+                    [] -> return T.empty
+                    (x:xs) -> do
+                        evalIterator (snd3 stmtId) a' (+1)
+                        evalStmt (SAssign var (EVal x) stmtId lineN)
+                        v' <- evalStmt body
+                        isSkip' <- loopStart (snd3 . sId $ foreachStmt)
+                        v'' <- foreach' (VArrayV $ Seq.fromList xs) isSkip'
+                        return $ T.append v' v''
+              foreach' x False = foreach' (VArrayV $ Seq.singleton x) False
+              foreach' _ True = return T.empty
+                    
+evalStmt (SBlock name body stmtId _) = do
+    (VHashV coderefs) <- getFromRefTableDef (VHashV Map.empty) "#coderefs"
+    let coderefs' = Map.insert name (VCode body) coderefs
+    refTableReplace "#coderefs" (VHashV coderefs')
+    return T.empty
+
+evalStmt (SProcess name assigns stmtId _) = do
+    mapM evalOrSkipStmt assigns
+    (VString name') <- evalExpr (EVal (VIString [name]))
+    evalNamedBlock (T.unpack name',stmtId)
+
+evalStmt (SWrapper name assigns body stmtId lineN) = do
+    (VString name') <- evalExpr (EVal (VIString [name]))
+    mapM evalOrSkipStmt assigns >> return T.empty
+    content <- evalOrSkipStmt body
+    evalStmt $ SAssign (Var [VarKey "content"]) (EVal (VString content)) (0,0,0) lineN
+    evalNamedBlock (T.unpack name',stmtId)
+
+evalStmt (SFilter name exprs body stmtId _) = do
+    v <- evalStmt body
+    case lookup name filters of
+        Nothing -> throwError ("Invalid filter '" ++ name ++ "'")
+        Just f -> f exprs v
+                          
+evalStmt (SLast (parent,_,_) _) = do
+    evalLastNext parent (-1)
+
+evalStmt (SNext (parent,_,_) _) = do
+    evalLastNext parent 1
+
+evalNamedBlock :: (String,StmtId) -> EvalWithExcept T.Text
+evalNamedBlock (name,stmtId) = do
+    (VHashV coderefs) <- getFromRefTableDef (VHashV Map.empty) "#coderefs"
+    case Map.lookup name coderefs of
+        (Just (VCode body)) -> evalOrSkipStmt body
+        Nothing -> do
+            (tStmtOrText, filename) <- readTemplateFile name
+            let stmtId' = (fst3 stmtId, (trd3 stmtId) + 1, (trd3 stmtId) + 1)
+            case tStmtOrText of
+                Left cache -> evalOrSkipStmt cache
+                Right ttext -> case parseTemplateWithStmtId ttext name stmtId' of
+                    Left err -> throwError (name ++ ": " ++ (show err))
+                    Right stmt -> do
+                        evalStmt (SBlock filename stmt stmtId' 0)
+                        catchError evalAndCache (\e -> throwError (name ++ ": " ++ e))
+                        where evalAndCache = do
+                                t <- evalOrSkipStmt stmt
+                                cacheDir <- getVarVal (Var [VarKey "_CONFIG", VarKey "CACHE_DIR"]) >>= \d -> return $ toString d
+                                liftIO $ if cacheDir /= "" then writeCache (cacheDir,name,stmt) else return ()
+                                return t
+                              writeCache (cacheDir,name,stmt) = do
+                                createDirectoryIfMissing True cacheDir 
+                                Bin.encodeFile (cacheDir ++ "/" ++ name) stmt
+                    
+readTemplateFile :: String -> EvalWithExcept (Either Stmt T.Text, String)
+readTemplateFile name = do
+    t <- getVarVal (Var [VarKey "_CONFIG", VarKey "TEMPLATES", VarKey name])
+    case t of
+        Undef -> throwError ("\"" ++ name ++ "\" not found")
+        tfn -> do
+            cacheDir <- getVarVal (Var [VarKey "_CONFIG", VarKey "CACHE_DIR"]) >>= \d -> return $ toString d
+            let filename = toString tfn
+            liftIO $ if cacheDir /= ""
+                then do
+                    let filenameCache = cacheDir ++ "/" ++ name
+                    fTime <- getModificationTime filename
+                    cfTime <- doesFileExist filenameCache >>= \ok -> if ok then getModificationTime filenameCache else return $ UTCTime (fromGregorian 1900 01 01) (secondsToDiffTime 0)
+                    if fTime > cfTime
+                        then TIO.readFile filename >>= \ttext -> return (Right ttext, filename)
+                        else ((Bin.decodeFile filenameCache) :: IO Stmt) >>= \cache -> return (Left cache, filename)
+                else TIO.readFile filename >>= \ttext -> return (Right ttext, filename)
+    
+evalLastNext :: Int -> Int -> EvalWithExcept T.Text
+evalLastNext parent breakCode = do
+    (VHashV loops) <- getFromRefTableDef (VHashV Map.empty) "#loopcontrols"
+    let loops' = Map.insert (show parent) (VInt $ fromIntegral breakCode) loops
+    refTableReplace "#loopcontrols" (VHashV loops')
+    return T.empty
+    
+loopStart :: Int -> EvalWithExcept Bool
+loopStart loopId = do
+    (VHashV loops) <- getFromRefTableDef (VHashV Map.empty) "#loopcontrols"
+    let (VInt breakCode) = Map.findWithDefault (VInt 0) (show loopId) loops
+        loops' = Map.insert (show loopId) (VInt $ if breakCode /= (-1) then 0 else (-1)) loops
+    refTableReplace "#loopcontrols" (VHashV loops')
+    return $ if breakCode == (-1) then True else False
+    
+loopEnd :: StmtId -> EvalWithExcept ()
+loopEnd loopId = do
+    (vars,reft) <- lift ask
+    liftIO $ HTIO.delete reft ("#foreacharr" ++ (show $ snd3 loopId))
+    liftIO $ HTIO.delete reft ("#foreach" ++ (show $ snd3 loopId))
+    (VHashV loops) <- getFromRefTableDef (VHashV Map.empty) "#loopcontrols"
+    let loops' = Map.insert (show $ snd3 loopId) (VInt 0) loops
+    refTableReplace "#loopcontrols" (VHashV loops')
+    (VArrayV outer) <- getFromRefTableDef (VArrayV Seq.empty) ("#foreacharr" ++ (show $ fst3 loopId))
+    if Seq.null outer
+        then liftIO $ HTIO.delete vars "loop"
+        else evalIterator (fst3 loopId) outer id
+    
+evalIterator :: Int -> Array -> (Val -> Val) -> EvalWithExcept ()
+evalIterator loopId arr iterFunc = do
+    (vars,_) <- lift ask
+    let l' = fromIntegral $ Seq.length arr
+        start = [("size",VInt l')
+                ,("max",VInt $ l' - 1)
+                ,("index",VInt (-1))
+                ]
+    (VHashV h') <- getFromRefTableDef (VHashV $ Map.fromList start) ("#foreach" ++ show loopId)
+    (VArrayV a') <- getFromRefTableDef (VArrayV arr) ("#foreacharr" ++ show loopId)
+    let (VInt i) = iterFunc (h' Map.! "index")
+        (VInt iMax) = h' Map.! "max"
+        h = flip Map.union h' (Map.fromList [("index",VInt i)
+                                           ,("count",VInt i+1)
+                                           ,("first",if i == 0 then VInt 1 else VInt 0)
+                                           ,("last",if i == iMax then VInt 1 else VInt 0)
+                                           ,("prev",if i == 0 then Undef else (a' `Seq.index` (fromIntegral i - 1)))
+                                           ,("next",if i == iMax then Undef else (a' `Seq.index` (fromIntegral i + 1)))
+                                           ,("odd",if (i + 1) `mod` 2 == 0 then VInt 0 else VInt 1)
+                                           ,("even",if (i + 1) `mod` 2 == 0 then VInt 1 else VInt 0)
+                                           ])
+    refTableReplace ("#foreach" ++ show loopId) (VHashV h)
+    refTableReplace ("#foreacharr" ++ show loopId) (VArrayV a')
+    liftIO $ HTIO.insert vars "loop" (VHashV h)
+    return ()
+
+--
+----- /statement evaluators
+--
+
+
+--
+----- common functions for vmethods and filters
+--
+
+_changefirst' s change = case T.unpack s of
+    x:xs -> T.pack $ (change x):xs
+    [] -> T.empty
+_Lcfirst' s = _changefirst' s toLower
+_Lower' = T.toLower
+_Ucfirst' s = _changefirst' s toUpper
+_Upper' = T.toUpper
+_Replace' s re su = case PCRE.substituteCompile' (BS.pack re) (BS.pack . T.unpack $ s) (BS.pack su) of
+                        Left err -> error err
+                        Right bs -> TE.decodeUtf8 $ bs
+_Trim' s = _Replace' s "(^\\s+|\\s+$)" ""
+_Collapse' s = _Trim' $ _Replace' s "\\s+" " "
+
+--
+----- /common functions for vmethods and filters
+--
+
+--
+----- vmethods
+--
+
+vmethods =
+    [("collapse",_collapse)
+    ,("defined",_defined)
+    ,("delete",_delete)
+    ,("each",_values)
+    ,("first",_first)
+    ,("grep",_grep)
+    ,("import",_import)
+    ,("item",_item)
+    ,("join",_join)
+    ,("keys",_keys)
+    ,("last",_last)
+    ,("lcfirst",_lcfirst)
+    ,("length",_length)
+    ,("lower",_lower)
+    ,("match",_match)
+    ,("nsort",_nsort)
+    ,("pairs",_pairs)
+    ,("pop",_pop)
+    ,("push",_push)
+    ,("remove",_remove)
+    ,("replace",_replace)
+    ,("reverse",_reverse)
+    ,("shift",_shift)
+    ,("size",_size)
+    ,("slice",_slice)
+    ,("sort",_sort)
+    ,("splice",_splice)
+    ,("split",_split)
+    ,("trim",_trim)
+    ,("ucfirst",_ucfirst)
+    ,("unique",_unique)
+    ,("unshift",_unshift)
+    ,("upper",_upper)
+    ,("values",_values)
+    
+    ] where
+    getter v _ r = return (r,v)
+    
+    _match' :: Val -> String -> [Val]
+    _match' v re = map (VString . TE.decodeUtf8) matches
+        where bs = (BS.pack . toString $ v) =~ re :: AllTextSubmatches [] BS.ByteString
+              matches = case getAllTextSubmatches bs of
+                    [] -> []
+                    (full:[]) -> [full]
+                    (_:subs) -> subs
+        
+    _matchGlobal' :: Val -> String -> [Val]
+    _matchGlobal' v re = case _match' (VString $ T.pack re) "\\(.+\\)" of
+        [] -> matches 
+        _ -> concat $ map (\m' -> _match' m' re) matches
+        where matches = map (VString . TE.decodeUtf8) $ getAllTextMatches ((BS.pack . toString $ v) =~ re :: AllTextMatches [] BS.ByteString)
+
+    _size' (VArrayV a) = fromIntegral $ Seq.length a
+    
+    _splice' a offs leng arr = (Seq.take offs'' a) Seq.>< arr Seq.>< (Seq.drop (offs'' + leng'') a)
+        where l = Seq.length a
+              offs' = if offs < 0 then l + offs + 1 else offs
+              offs'' = if offs' < 0 then 0 else offs'
+              leng' = if leng < 0 then (l - offs'' + leng) else leng
+              leng'' = if leng' < 0 then 0 else leng'
+ 
+    _collapse s _ r = return (r, VString . _Collapse' . toText $ s)
+    
+    _defined h@(VHashV _) (k:_) r = do
+        k' <- evalExpr k
+        (r',v) <- getVal (r,h) (VarKey . toString $ k')
+        _defined v [] r
+    _defined a@(VArrayV _) (i:_) r = do
+        i' <- evalExpr i
+        (r',v) <- getVal (r,a) (VarIndex . toInt $ i')
+        _defined v [] r
+    _defined Undef _ r = return (r,VInt 0)
+    _defined _ _ r = return (r,VInt 1)
+    
+    _delete _ [] r = return (r,Undef)
+    _delete (VHashV h) (e:es) r = do
+        k <- evalExpr e
+        let vh = VHashV $ Map.delete (toString k) h
+        refTableReplace r vh
+        _delete vh es r
+    
+    _each (VHashV h) _ r = do
+        return (r,arr)
+            where arr = VArrayV . Seq.fromList . concat . map (\(k,v) -> [(VString . T.pack $ k),v]) $ (Map.toList h)
+    _each _ _ r = return (r,Undef)
+    
+    _first a@(VArrayV a') [] r = if (_size' a) > 0 then return (r,a' `Seq.index` 0) else return (r,Undef)
+    _first (VArrayV a) (e:_) r = do
+        i <- evalExpr e
+        return (r, VArrayV $ Seq.take (toInt i) a)
+    _first _ _ r = return (r,Undef)
+    
+    _grep (VArrayV a) (re:_) r = do
+        re' <- liftM toString (evalExpr re)
+        let a' = filter (\v -> not . null $ _match' v re') (toList a)
+        return (r, VArrayV . Seq.fromList $ a')
+    
+    _import (VHashV h) (e:es) r = do
+        v <- evalExpr e
+        case v of
+            (VRef r') -> do
+                v' <- getFromRefTable r'
+                _import (VHashV h) ((EVal v'):es) r
+            (VHashV h') -> do
+                let vh = VHashV $ Map.union h' h
+                refTableReplace r vh
+                _import vh es r
+            _ -> _import (VHashV h) es r
+    _import (VArrayV a) (e:es) r = do
+        v <- evalExpr e
+        case v of
+            (VRef r') -> do
+                v' <- getFromRefTable r'
+                _import (VArrayV a) ((EVal v'):es) r
+            (VArrayV a') -> do
+                let va = VArrayV $ a Seq.>< a'
+                refTableReplace r va
+                _import va es r
+            _ -> _import (VArrayV a) es r
+    _import _ _ r = return (r,Undef)
+            
+    _item h@(VHashV _) (e:_) r = do
+        k <- evalExpr e
+        (_,v) <- getVal (r,h) (VarKey .toString $ k)
+        return (r,v)
+    _item _ _ r = return (r,Undef)
+        
+    _join (VArrayV a) (e:_) r = do
+        delim <- evalExpr e
+        return(r,VString . T.pack . (intercalate $ toString delim) . (map toString) . toList $ a)
+    _join a@(VArrayV _) [] r = _join a [(EVal . VString . T.pack $ ",")] r
+    _join _ _ r = return (r,Undef)
+    
+    _keys (VHashV h) _ r = do
+        return (r,arr)
+            where arr = VArrayV . Seq.fromList . map (VString . T.pack) $ (Map.keys h)
+    _keys _ _ r = return (r,Undef)
+    
+    _last a@(VArrayV a') [] r = if (_size' a) > 0 then return (r,a' `Seq.index` ((_size' a) - 1)) else return (r,Undef)
+    _last (VArrayV a) (e:_) r = do
+        i <- evalExpr e
+        return (r, VArrayV . Seq.reverse $ Seq.take (toInt i) (Seq.reverse a))
+    _last _ _ r = return (r,Undef)
+    
+    _lcfirst s = getter $ VString (_Lcfirst' . toText $ s)
+
+    _length s = getter $ VInt (fromIntegral . length . toString $ s)
+
+    _lower s = getter $ VString (_Lower' . toText $ s)
+    
+    _match v (re:gl:_) r = do
+        re' <- liftM toString (evalExpr re)
+        gl' <- evalExpr gl
+        let matcher = if isValTrue gl' then _matchGlobal' else _match'
+        case matcher v re' of
+            [] -> return (r,Undef)
+            ms -> do
+                return (r,VArrayV . Seq.fromList $ ms)
+    _match v (re:[]) r = _match v [re, EVal . VInt $ 0] r
+    _match _ _ r = return (r,Undef)
+            
+    _nsort (VArrayV a) _ r = do
+        return (r,arr)
+            where arr = VArrayV . Seq.fromList . sort . (map toNum) . toList $ a
+    _nsort _ _ r = return (r,Undef)
+    
+    _pairs (VHashV h) _ r = do
+        return (r,arr)
+            where arr = VArrayV . Seq.fromList . map
+                        (\(k,v) -> VHashV . Map.fromList $ [("key",VString . T.pack $ k),("value",v)]) $ Map.toList h
+    _pairs _ _ r = return (r,Undef)
+    
+    _pop (VArrayV a) [] r = do
+        (_,v) <- _last (VArrayV a) [] r
+        let a' = VArrayV $ _splice' a (-2) 1 Seq.empty
+        refTableReplace r a'
+        return (r,v)
+    _pop _ _ r = return (r,Undef)
+
+    _push (VArrayV a) exs r = do
+        vxs <- mapM evalExpr exs
+        let a' = VArrayV $ _splice' a (-1) 0 (Seq.fromList vxs)
+        refTableReplace r a'
+        return (r,Undef)
+    _push _ _ r = return (r,Undef)
+
+    _remove v (re:_) r = _replace v [re] r
+    _remove _ _ r = return (r,Undef)
+    
+    _replace v (re:su:_) r = do
+        re' <- liftM toString (evalExpr re)
+        su' <- liftM toString (evalExpr su)
+        let v' = _Replace' (toText v) re' su'
+        return (r,VString v')
+    _replace v (re:_) r = _replace v [re, EVal . VString $ T.empty] r
+    _replace _ _ r = return (r,Undef)
+    
+    _reverse (VArrayV a) [] r = do
+        return (r,VArrayV . Seq.reverse $ a)
+    _reverse _ _ r = return (r,Undef)
+    
+    _shift (VArrayV a) [] r = do
+        (_,v) <- _first (VArrayV a) [] r
+        let a' = VArrayV $ _splice' a 0 1 Seq.empty
+        refTableReplace r a'
+        return (r,v)
+    _shift _ _ r = return (r,Undef)
+    
+    _size (VHashV h) _ r = return (r,VInt . fromIntegral . length . toList $ h)
+    _size a@(VArrayV _) _ r = return (r,VInt (_size' a))
+    _size _ _ r = return (r,VInt 0)
+    
+    _slice (VArrayV a) (fr:to:_) r = do
+        fr' <- liftM toInt (evalExpr fr)
+        to' <- liftM toInt (evalExpr to)
+        let l = Seq.length a
+            fr'' = if fr' < 0 then l + fr' else fr'
+            to'' = if to' < 0 then l + to' else to'
+            a' = if fr'' < 0 || to'' < 0 || fr'' > to'' || fr'' > l || to'' > l then Seq.empty
+                 else (Seq.take (to'' - fr'' + 1)) . (Seq.drop fr'') $ a
+        return (r,VArrayV a')
+    _slice (VArrayV a) (fr:_) r = _slice (VArrayV a) [fr,EVal . VInt $ (-1)] r
+    _slice _ _ r = _slice (VArrayV Seq.empty) (map (EVal . VInt) [0,0]) r
+            
+        
+    _sort (VArrayV a) _ r = do
+        return (r,arr)
+            where arr = VArrayV . Seq.fromList . sort . (map (VString . T.pack . toString)) . toList $ a
+    _sort _ _ r = return (r,Undef)
+    
+    _splice (VArrayV a) (eo:el:exs) r = do
+        offs <- liftM toInt (evalExpr eo)
+        leng <- liftM toInt (evalExpr el)
+        vxs <- mapM evalExpr exs
+        arr <- mapM mkArrEl vxs
+        let arr' = VArrayV $ _splice' a offs leng (Seq.fromList $ concat arr)
+        refTableReplace r arr'
+        return (r,Undef)
+            where mkArrEl :: Val -> EvalWithExcept [Val]
+                  mkArrEl (VRef r') = do
+                        v <- getFromRefTable r'
+                        mkArrEl v
+                  mkArrEl (VArrayV a') = return (toList a')
+                  mkArrEl v = return [v]
+    _splice a@(VArrayV a') (eo:[]) r = _splice a [eo,(EVal . VInt . fromIntegral . Seq.length $ a')] r
+    _splice a@(VArrayV a') [] r = _splice a [EVal (VInt 0)] r
+    _splice _ _ r = return (r,Undef)
+    
+    _split v (re:_) r = do
+        re' <- liftM toString (evalExpr re)
+        let a = case PCRE.splitCompile' (BS.pack re') (BS.pack . toString $ v) of
+                    Left err -> error err
+                    Right vs -> VArrayV . Seq.fromList . (map (VString . TE.decodeUtf8)) $ vs
+        return (r,a)
+    _split v _ r = return (r,VArrayV . Seq.singleton . VString . toText $ v)
+    
+    _trim s _ r = return (r, VString . _Trim' . toText $ s)
+
+    _ucfirst s = getter $ VString (_Ucfirst' . toText $ s)
+    
+    _unique (VArrayV a) _ r = do
+        return (r,arr)
+            where arr = VArrayV . Seq.fromList . (nubBy (\x y -> toString x == toString y)) . toList $ a
+
+    _unshift (VArrayV a) exs r = do
+        vxs <- mapM evalExpr exs
+        let a' = VArrayV $ _splice' a 0 0 (Seq.fromList vxs)
+        refTableReplace r a'
+        return (r,Undef)
+    _unshift _ _ r = return (r,Undef)
+
+    _upper s = getter $ VString (_Upper' . toText $ s)
+
+    _values (VHashV h) _ r = do
+        return (r, VArray . map EVal $ (Map.elems h))
+    _values _ _ r = return (r,Undef)
+
+
+--
+----- /vmethods
+--
+
+
+--
+----- filters
+--
+
+filters =
+    [("collapse",_collapse)
+    ,("html",_html)
+    ,("lcfirst",_lcfirst)
+    ,("lower",_lower)
+    ,("null",_null)
+    ,("replace",_replace)
+    ,("trim",_trim)
+    ,("ucfirst",_ucfirst)
+    ,("upper",_upper)
+    ,("uri",_uri)
+    ] where
+    _collapse _ v = return $ _Collapse' v
+    _html _ s = return $ foldl' (\s' (re,su) -> _Replace' s' re su) s [("&","&amp;"),("<","&lt;"),(">","&gt;"),("\"","&quot;")]
+    _lcfirst _ v = return $ _Lcfirst' v
+    _lower _ v = return $ _Lower' v
+    _null _ _ = return T.empty
+    _replace [] v = return v
+    _replace (re:[]) v = _replace [re, EVal . VString $ T.empty] v
+    _replace (re:su:_) v = do
+        re' <- liftM toString (evalExpr re)
+        su' <- liftM toString (evalExpr su)
+        let v' = _Replace' v re' su'
+        return v'
+    _trim _ v = return $ _Trim' v
+    _ucfirst _ v = return $ _Ucfirst' v
+    _upper _ v = return $ _Upper' v
+    _uri _ v = return $ URI.encodeText v
+
+--
+----- /filters
+--
+    
+data TConfig = AesonObject Aeson.Value | JSONstring T.Text
+
+evalTemplateFile ::     TName -- ^ Template filename to process
+                        -> TConfig -- ^ Template config and initial variables (either aeson object or JSON object string)
+                        -> IO (Either TErr T.Text) -- ^ Result of template evaluation - either error or text
+evalTemplateFile t cfg = do
+    case cfg of
+        (JSONstring json) -> case Aeson.decode (TLE.encodeUtf8 . TL.fromStrict $ json) of
+                                (Just aeson) -> evalTemplateFile t (AesonObject aeson)
+                                _ -> return $ Left "Invalid JSON string"
+        (AesonObject aeson) -> case fromAeson aeson of
+                                (EVal (VHash h)) -> do
+                                    vars <- HTIO.new
+                                    reft <- HTIO.new
+                                    runReaderT (runExceptT (evalTemplateFile' t h)) (vars,reft)
+                                _ -> return $ Left "Aeson 'object' expected as a second parameter"
+                                
+
+
+evalTemplateFile' :: TName -> [(String,Expr)] -> EvalWithExcept T.Text
+evalTemplateFile' t cfg = do
+    let assignVar (k,e) = evalStmt (SAssign (Var [VarKey k]) e (0,0,0) 0)
+    mapM_ assignVar cfg
+    vDirs <- getVarVal (Var [VarKey "_CONFIG", VarKey "INCLUDE_PATH"])
+    let dirs = case vDirs of
+                (VArrayV a) -> toList a
+                s@(VString _) -> [s]
+                _ -> []
+        getFilesPaths d = do
+                f' <- (listDirectory d) >>= mapM (\f_ -> do {dabs_ <- makeAbsolute (d ++ "/" ++ f_); return (f_,dabs_)})
+                f'' <- filterM (doesFileExist . snd) f'
+                return f''
+    files <- mapM (liftIO . getFilesPaths) (map toString dirs)
+    let files' = map (\(k,v) -> (k, VString . T.pack $ v)) (concat files)
+    evalStmt $ SAssign (Var [VarKey "_CONFIG", VarKey "TEMPLATES"]) (EVal (VHashV . Map.fromList $ files')) (0,0,0) 0
+    evalStmt $ SProcess (IString $ T.pack t) [] (0,0,0) 0
+    
+{-$conf
+All variables in initial 'TConfig' object are passed to the parsed template.
+
+Special /__\_CONFIG__/ variable is an object that contains settings passed to the template evaluator:
+    /__INCLUDE_PATH__/ - list of directories where evaluator will look for template files.
+    /__CACHE_DIR__/ - path to cache templates to.
+-}
+
+{-$example
+Template Toolkit language manual: "Text.TemplateToolkitLang".
+
+Below is a simple example of using this module:
+
+=== template-toolkit-example.hs
+@
+import Text.TemplateToolkit
+import qualified Data.Text.IO as TIO (readFile)
+import qualified Data.Text as T
+
+main = do
+    cfg <- TIO.readFile "./conf.json"
+    s <- evalTemplateFile "template.tt" (JSONstring cfg)
+    case s of
+        (Right txt) -> putStr . T.unpack $ txt
+        (Left err) -> putStr ("ERROR! " ++ err)
+@
+
+=== conf.json
+@
+{
+  \"_CONFIG\":{
+    \"INCLUDE_PATH\":[\".\"]
+  },
+  \"users\":{
+    \"Foo\": 13,
+    \"Bar\": 3.14,
+    \"Baz\": \"bazzz\"
+  }
+}
+@
+
+=== template.tt
+@
+\<html>
+  \<body>
+    \<h1>Template Toolkit for Haskell\</h1>
+    \<h2>Count 1-10:\</h2>
+    [% FOREACH i = [1..10] -%]
+      [% i; (!loop.last) ? \', \' : \'.\' %]
+    [%- END %]
+    \<h2>Users hash:\</h2>
+    [% FOREACH user = users.pairs %]
+      \<p>[% user.key %]: [% user.value %]
+    [% END %]
+    \<h2>External template:\</h2>
+    [% PROCESS template2.tt words = [\'dog\',\'cat\',\'pig\'] %]
+  \</body>
+\</html>
+@
+
+=== template2.tt
+@
+\<p>[% words.sort.reverse.join(\'|\') %]
+@
+-}
+ src/Text/TemplateToolkitAST.hs view
@@ -0,0 +1,516 @@+{-# LANGUAGE DeriveGeneric #-}
+module Text.TemplateToolkitAST (
+    Array, StmtId, TName, IString(..), Val(..), VarNode(..), Var(..), Expr(..), UnOp(..), BinOp(..), Else(..), Stmt(..)
+    ,parseTemplateWithStmtId ,parseTemplate) where
+
+import GHC.Generics (Generic)
+import Text.Parsec
+import Text.Parsec.Text
+import Text.Parsec.Expr
+import Control.Applicative ((<*))
+import Control.Monad
+import Numeric
+import Data.List (foldl')
+import Data.Char (toUpper,toLower,isDigit)
+import qualified Data.Text as T
+import qualified Data.Map.Strict as Map
+import qualified Data.Sequence as Seq
+import qualified Data.Binary as Bin
+
+--
+----- types
+--
+
+type Hash = Map.Map String Val
+type Array = Seq.Seq Val
+type StmtId = (Int,Int,Int) -- (ParentID,SelfID,LastID)
+type LineN = Int
+type TName = String
+type Parser' = GenParser (TName,StmtId)
+
+data IString = IString T.Text | IVar Var deriving (Show, Generic)
+
+data Val = VString T.Text
+         | VIString [IString]
+         | VInt Integer
+         | VFloat Double
+         | VArray [Expr] -- array of Expressions (must be eval'ed)
+         | VArrayRange Expr Expr -- range from Expr1 to Expr2 (must be eval'ed)
+         | VArrayV Array -- array of Values (eval'ed and can be stored)
+         | VHash [(String,Expr)] -- hash of Expressions
+         | VHashV Hash  -- hash of Values
+         | VRef String -- reference to VArrayV or VHashV
+         | VCode Stmt
+         | Undef
+         deriving (Show, Generic)
+        
+data VarNode = VarKey String
+             | VarIndex Int
+             | VarMethod String [Expr]
+             | VarRef Var
+             deriving (Show, Generic)
+             
+data Var = Var [VarNode] deriving (Show, Generic)
+
+data Expr = EVal Val
+          | EVar Var
+          | EAssign Var Expr
+          | EUnOp UnOp Expr
+          | EBinOp BinOp Expr Expr
+          | ETerOp Expr Expr Expr
+          deriving (Show, Generic)
+        
+data UnOp = Pos | Neg | Not
+            deriving (Show, Generic)
+
+data BinOp = Add | Sub | Mul | Div | Mod | Con | Gt | Ge | Lt | Le | Eq | Ne | And | Or
+             deriving (Show, Generic)
+          
+data Else = Else Stmt | Elsif Expr Stmt (Maybe Else)
+    deriving (Show, Generic)
+
+data Stmt = Seq {sSeq :: [Stmt], sId :: StmtId, lineN :: LineN}
+          | SComment {sId :: StmtId, lineN :: LineN}
+          | SText {sText :: T.Text, sId :: StmtId, lineN :: LineN}
+          | SIf {sCond :: Expr, sBody :: Stmt, sElse :: (Maybe Else), sId :: StmtId, lineN :: LineN}
+          | SWhile {sCond :: Expr, sBody :: Stmt, sId :: StmtId, lineN :: LineN}
+          | SForeach {sForeachVar :: Var, sForeachArr :: Expr, sBody :: Stmt, sId :: StmtId, lineN :: LineN}
+          | SNext {sId :: StmtId, lineN :: LineN}
+          | SLast {sId :: StmtId, lineN :: LineN}
+          | SBlock {sBlockName :: String, sBody :: Stmt, sId :: StmtId, lineN :: LineN}
+          | SProcess {sProcessName :: IString, sProcessAssigns :: [Stmt], sId :: StmtId, lineN :: LineN}
+          | SWrapper {sWrapperName :: IString, sWrapperAssigns :: [Stmt], sBody :: Stmt, sId :: StmtId, lineN :: LineN}
+          | SAssign {sAssignVar :: Var, sAssignExpr :: Expr, sId :: StmtId, lineN :: LineN}
+          | SExpr {sExpr :: Expr, sId :: StmtId, lineN :: LineN}
+          | SFilter {sFilterName :: String, sFilterParams :: [Expr], sBody :: Stmt, sId :: StmtId, lineN :: LineN}
+          deriving (Show, Generic)
+
+instance Bin.Binary IString
+instance Bin.Binary Var
+instance Bin.Binary VarNode
+instance Bin.Binary Val
+instance Bin.Binary Expr
+instance Bin.Binary UnOp
+instance Bin.Binary BinOp
+instance Bin.Binary Else
+instance Bin.Binary Stmt
+
+          
+--
+----- /types
+--
+
+--
+----- parsers
+--
+spaces1 :: Parser' ()
+spaces1 = skipMany1 space
+
+spacesAround :: Parser' a -> Parser' a
+spacesAround = between spaces spaces
+
+spaces1Around :: Parser' a -> Parser' a
+spaces1Around = between spaces1 spaces1
+
+parens :: Parser' a -> Parser' a
+parens = between (char '(' >> spaces) (spaces >> char ')')
+
+charCi :: Char -> Parser' Char
+charCi c = (char $ toUpper c) <|> (char $ toLower c)
+
+stringCi :: String -> Parser' String
+stringCi s = mapM charCi s
+
+escapedChar :: Parser' Char
+escapedChar = do
+    char '\\'
+    x <- oneOf "\\$\"'nt"
+    return $ case x of
+        '\\' -> x
+        '$' -> x
+        '"' -> x
+        '\'' -> x
+        'n' -> '\n'
+        't' -> '\t'
+
+parseString :: Parser' Val
+parseString = do
+    char '\''
+    x <- many $ noneOf "'\\" <|> escapedChar
+    char '\''
+    return $ VString (T.pack x)
+    
+parseInterpolatedVar :: Parser' Var
+parseInterpolatedVar = try $ (try $ between (string "${") (char '}') parseVar) <|> (char '$' >> parseVar)
+
+parseInterpolatedString :: Parser' Val
+parseInterpolatedString = do
+    char '"'
+    iStrings <- many $ ((many1 $ noneOf "\"$\\" <|> escapedChar) >>= return . IString . T.pack) <|> (parseInterpolatedVar >>= return . IVar)
+    char '"'
+    return $ VIString iStrings
+
+parseInt :: Parser' Val
+parseInt = do
+    num <- many1 digit
+    return $ VInt (read num)
+
+parseFloat :: Parser' Val
+parseFloat = try $ do
+    d1 <- many1 digit
+    char '.'
+    d2 <- many1 digit
+    return $ VFloat (fst $ (readFloat $ d1++"."++d2) !! 0)
+
+parseArray :: Parser' Val
+parseArray = do
+    char '[' >> spaces >> many (char ',' >> spaces)
+    list <- sepEndBy parseExpr (many $ spacesAround (char ','))
+    spaces >> char ']'
+    return $ VArray list
+    
+parseArrayRange :: Parser' Val
+parseArrayRange = try $ do
+    char '[' >> spaces
+    e1 <- parseExpr
+    spacesAround $ string ".."
+    e2 <- parseExpr
+    spaces >> char ']'
+    return $ VArrayRange e1 e2
+
+parseHashKeyVal :: Parser' (String,Expr)
+parseHashKeyVal = do
+    key <- (parseString >>= \(VString str) -> return $ T.unpack str) <|> many1 alphaNum
+    spaces >> string "=>" >> spaces
+    val <- parseExpr
+    return (key,val)
+    
+parseHash :: Parser' Val
+parseHash = do
+    char '{' >> spaces >> many (char ',' >> spaces)
+    list <- sepEndBy parseHashKeyVal (many $ spacesAround (char ','))
+    spaces >> char '}'
+    return $ VHash list
+
+parseVarKey :: Parser' VarNode
+parseVarKey = try $ do
+    let varletter' = ['A'..'Z']++['a'..'z']++['_']
+    c <- oneOf varletter'
+    cs <- many $ oneOf (varletter' ++ ['0'..'9'])
+    if reserved' (c:cs) then unexpected "reserved"
+    else return $ VarKey (c:cs)
+        where reserved' n = elem n ["BLOCK", "ELSE", "ELSIF", "END", "EXIT", "FILTER", "FOREACH", "IF", "LAST", "NEXT", "PROCESS", "WHILE", "WRAPPER"]
+    
+parseVarIndex :: Parser' VarNode
+parseVarIndex = liftM (VarIndex . read) $ many1 digit
+    
+parseVarMethod :: Parser' VarNode
+parseVarMethod = try $ do
+    VarKey methodName <- parseVarKey
+    exprs <- parens $ sepBy parseExpr (spacesAround (char ','))
+    return $ VarMethod methodName exprs
+    
+parseVarRef :: Parser' VarNode
+parseVarRef = do
+    char '$'
+    var <- parseVarKey
+    return $ VarRef (Var [var])
+
+parseVarRefDeep :: Parser' VarNode
+parseVarRefDeep = do
+    try $ string "${"
+    root <- parseVarKey
+    deeper <- many $ char '.' >> (parseVarKey <|> parseVarIndex)
+    char '}'
+    return $ VarRef (Var (root:deeper))
+    
+parseVar :: Parser' Var
+parseVar = do
+    root <- (parseVarMethod <|> parseVarKey)
+    deeper <- many $ char '.' >> (parseVarMethod <|> parseVarRefDeep <|> parseVarRef <|> parseVarKey <|> parseVarIndex)
+    return $ Var (root:deeper)
+
+parseMethodItem' :: Parser' VarNode
+parseMethodItem' = try $ do
+    string "item"
+    exprs <- parens $ sepBy parseExpr (spacesAround (char ','))
+    return $ VarMethod "item" exprs
+    
+parseStrictVar :: Parser' Var
+parseStrictVar = do
+    root <- parseVarKey
+    deeper <- many $ char '.' >> (parseMethodItem' <|> parseVarRefDeep <|> parseVarRef <|> parseVarKey <|> parseVarIndex)
+    return $ Var (root:deeper)
+    
+parseVal :: Parser' Expr
+parseVal = (liftM EVal $ parseHash <|> parseArrayRange <|> parseArray <|> parseString <|> parseInterpolatedString)
+            <|> (liftM EVar parseVar)
+            <|> (liftM EVal $ parseFloat <|> parseInt)
+
+parseTernary :: Parser' Expr
+parseTernary = try $ do
+    cond <- parseTerm
+    spacesAround $ char '?'
+    yes <- parseTerm
+    spacesAround $ char ':'
+    no <- parseTerm
+    return $ ETerOp cond yes no
+
+parseTerm :: Parser' Expr
+parseTerm = ((parens parseExpr) <|> parseVal) <* spaces
+    
+parseExpr = buildExpressionParser table (parseTernary <|> parseTerm)
+    where table =   [
+                    [Prefix $ try (char '-' >> notFollowedBy (char '%')) >> return (EUnOp Neg),
+                    Prefix $ char '+' >> return (EUnOp Pos),
+                    Prefix $ ((string "!" >> return ()) <|> try (stringCi "not" >> spaces1)) >> return (EUnOp Not)]
+                    ,[Infix (char '*' >> spaces >> return (EBinOp Mul)) AssocLeft,
+                    Infix (char '/' >> spaces >> return (EBinOp Div)) AssocLeft,
+                    Infix (try (char '%' >> notFollowedBy (char ']') >> spaces) >> return (EBinOp Mod)) AssocLeft,
+                    Infix (try (stringCi "mod" >> spaces1) >> return (EBinOp Mod)) AssocLeft]
+                    ,[Infix (char '+' >> spaces >> return (EBinOp Add)) AssocLeft,
+                    Infix (try (char '-' >> notFollowedBy (char '%') >> spaces) >> return (EBinOp Sub)) AssocLeft]
+                    ,[Infix (try (char '_' >> spaces1) >> return (EBinOp Con)) AssocLeft]
+                    ,[Infix (try (string ">=" >> spaces) >> return (EBinOp Ge)) AssocLeft,
+                    Infix (char '>' >> spaces >> return (EBinOp Gt)) AssocLeft,
+                    Infix (try (string "<=" >> spaces) >> return (EBinOp Le)) AssocLeft,
+                    Infix (char '<' >> spaces >> return (EBinOp Lt)) AssocLeft,
+                    Infix (try (string "==" >> spaces) >> return (EBinOp Eq)) AssocLeft,
+                    Infix (string "!=" >> spaces >> return (EBinOp Ne)) AssocLeft]
+                    ,[Infix (try (stringCi "gt" >> spaces1) >> return (EBinOp Gt)) AssocLeft,
+                    Infix (try (stringCi "ge" >> spaces1) >> return (EBinOp Ge)) AssocLeft,
+                    Infix (try (stringCi "lt" >> spaces1) >> return (EBinOp Lt)) AssocLeft,
+                    Infix (try (stringCi "le" >> spaces1) >> return (EBinOp Le)) AssocLeft,
+                    Infix (try (stringCi "eq" >> spaces1) >> return (EBinOp Eq)) AssocLeft,
+                    Infix (try (stringCi "ne" >> spaces1) >> return (EBinOp Ne)) AssocLeft]
+                    ,[Infix ((try (stringCi "and" >> spaces1) <|> (spacesAround (string "&&") >> return ())) >> return (EBinOp And)) AssocLeft,
+                    Infix ((try (stringCi "or" >> spaces1) <|> (try (spacesAround (string "||")) >> return ())) >> return (EBinOp Or)) AssocLeft]
+                    ]
+
+incrementSid :: Parser' StmtId
+incrementSid = do
+    (tName,(parent,prev,_)) <- getState
+    let newId = (parent,prev+1,prev+1)
+    putState (tName,newId)
+    return newId
+    
+getLineN :: Parser' LineN
+getLineN = liftM sourceLine getPosition
+
+parseComment :: Parser' Stmt
+parseComment = do
+    char '#'
+    lineN <- getLineN
+    manyTill anyChar (string "\n" <|> try (string "-%]" <|> string "%]"))
+    newId <- incrementSid
+    return $ SComment newId lineN
+    
+parseText :: Parser' Stmt
+parseText = do
+    spaces
+    try $ (string "-%]" <* spaces) <|> string "%]"
+    lineN <- getLineN
+    s <- manyTill anyChar (try ((try $ spaces >> string "[%-") <|> string "[%"))
+    spaces
+    newId <- incrementSid
+    return $ SText (T.pack s) newId lineN
+
+stmtEnd :: Parser' ()
+stmtEnd = do
+    spaces
+    eof <|> (char ';' >> spaces) <|> (lookAhead (string "-%]" <|> string "%]") >> return ())
+    
+parsePipeFilter :: Parser' (String,[Expr])
+parsePipeFilter = do
+    try $ spaces >> char '|' >> spaces1
+    (VarKey name) <- parseVarKey
+    exprs <- try (parens $ sepBy parseExpr (spacesAround (char ','))) <|> return []
+    return (name,exprs)
+    
+stmtEndFilter :: Parser' [(String,[Expr])]
+stmtEndFilter = do
+    spaces
+    fltrs <- many parsePipeFilter
+    eof <|> (char ';' >> spaces) <|> (lookAhead (string "-%]" <|> string "%]") >> return ())
+    return fltrs
+
+parseEnd :: Parser' ()
+parseEnd = (try $ string "END") >> stmtEnd
+
+parseMaybeElse :: Parser' (Maybe Else)
+parseMaybeElse = ((parseElsif <|> parseElse) >>= \els -> return $ Just els) <|> (parseEnd >> return Nothing)
+
+parseIf :: Parser' Stmt
+parseIf = do
+    try $ string "IF" >> spaces1
+    lineN <- getLineN
+    newId <- incrementSid
+    cond <- parseExpr
+    stmtEnd
+    body <- parseStmtSeq
+    maybeElse <- parseMaybeElse
+    return $ SIf cond body maybeElse newId lineN
+    
+parseElsif :: Parser' Else
+parseElsif = do
+    try $ string "ELSIF" >> spaces1
+    cond <- parseExpr
+    stmtEnd
+    body <- parseStmtSeq
+    maybeElse <- parseMaybeElse
+    return $ Elsif cond body maybeElse
+
+parseElse :: Parser' Else
+parseElse = do
+    try $ string "ELSE" >> stmtEnd
+    body <- parseStmtSeq
+    parseEnd
+    return $ Else body
+
+parseWhile :: Parser' Stmt
+parseWhile = do
+    try $ string "WHILE" >> spaces1
+    lineN <- getLineN
+    (tName,(parent,prev,_)) <- getState
+    putState (tName,(prev+1,prev+1,prev+1))
+    cond <- parseExpr
+    stmtEnd
+    body <- parseStmtSeq
+    parseEnd
+    return $ SWhile cond body (parent,prev+1,prev+1) lineN
+
+parseForeach :: Parser' Stmt
+parseForeach = do
+    try $ string "FOREACH" >> spaces1
+    lineN <- getLineN
+    (tName,(parent,prev,_)) <- getState
+    putState (tName,(prev+1,prev+1,prev+1))
+    (SAssign var expr _ _) <- parseAssign
+    stmtEnd
+    body <- parseStmtSeq
+    parseEnd
+    return $ SForeach var expr body (parent,prev+1,prev+1) lineN
+
+parseLast :: Parser' Stmt
+parseLast = do
+    try $ string "LAST" >> stmtEnd
+    lineN <- getLineN
+    newId <- incrementSid
+    return $ SLast newId lineN
+    
+parseNext :: Parser' Stmt
+parseNext = do
+    try $ string "NEXT" >> stmtEnd
+    lineN <- getLineN
+    newId <- incrementSid
+    return $ SNext newId lineN
+    
+parseBlock :: Parser' Stmt
+parseBlock = do
+    try $ string "BLOCK" >> spaces1
+    lineN <- getLineN
+    newId <- incrementSid
+    (VarKey name) <- parseVarKey
+    stmtEnd
+    body <- parseStmtSeq
+    parseEnd
+    return $ SBlock name body newId lineN
+
+parseTmplName :: Parser' IString
+parseTmplName = do
+    (liftM IVar parseInterpolatedVar) <|> do
+        (VarKey s) <- parseVarKey
+        ext <- option "" (many $ oneOf (['A'..'Z']++['a'..'z']++['_','.','-']))
+        return $ IString (T.pack (s ++ ext))
+
+parseProcess :: Parser' Stmt
+parseProcess = do
+    try $ string "PROCESS" >> spaces1
+    lineN <- getLineN
+    newId <- incrementSid
+    name <- parseTmplName
+    spaces
+    assigns <- many parseSimpleAssign
+    fltr <- stmtEndFilter
+    return $ case fltr of
+        [] -> SProcess name assigns newId lineN
+        fltrs -> foldr (\(fltrName,exprs) stmt -> SFilter fltrName exprs stmt newId lineN) (SProcess name assigns newId lineN) fltrs
+
+parseWrapper :: Parser' Stmt
+parseWrapper = do
+    try $ string "WRAPPER" >> spaces1
+    lineN <- getLineN
+    newId <- incrementSid
+    name <- parseTmplName
+    spaces
+    assigns <- many parseSimpleAssign
+    stmtEnd
+    body <- parseStmtSeq
+    parseEnd
+    return $ SWrapper name assigns body newId lineN
+        
+parseAssign :: Parser' Stmt
+parseAssign = do
+    lineN <- getLineN
+    var <- parseStrictVar
+    spacesAround $ char '='
+    expr <- parseExpr
+    newId <- incrementSid
+    return $ SAssign var expr newId lineN
+    
+parseSimpleAssign :: Parser' Stmt
+parseSimpleAssign = do
+    lineN <- getLineN
+    var <- parseStrictVar
+    spacesAround $ char '='
+    expr <- parseTernary <|> parseTerm
+    newId <- incrementSid
+    return $ SAssign var expr newId lineN
+
+parseStmtExpr :: Parser' Stmt
+parseStmtExpr = do
+    lineN <- getLineN
+    expr <- parseExpr
+    fltr <- stmtEndFilter
+    newId <- incrementSid
+    return $ case fltr of
+        [] -> SExpr expr newId lineN
+        fltrs -> foldl' (\stmt (fltrName,exprs) -> SFilter fltrName exprs stmt newId lineN) (SExpr expr newId lineN) fltrs
+        
+parseFilter :: Parser' Stmt
+parseFilter = do
+    try $ string "FILTER" >> spaces1
+    lineN <- getLineN
+    newId <- incrementSid
+    (VarKey name) <- parseVarKey
+    exprs <- try (parens $ sepBy parseExpr (spacesAround (char ','))) <|> return []
+    stmtEnd
+    body <- parseStmtSeq
+    parseEnd
+    return $ SFilter name exprs body newId lineN
+    
+parseStmtSeq :: Parser' Stmt
+parseStmtSeq = do
+    newId <- incrementSid
+    lineN <- getLineN
+    seq <- many1 (
+        parseComment <|>
+        parseText <|>
+        parseIf <|>
+        parseWhile <|>
+        parseForeach <|>
+        parseLast <|>
+        parseNext <|>
+        parseBlock <|>
+        parseProcess <|>
+        parseWrapper <|>
+        parseFilter <|>
+        (try $ parseAssign <* stmtEnd) <|>
+        parseStmtExpr
+        )
+    return $ Seq seq newId lineN
+    
+parseTemplateWithStmtId :: T.Text -> TName -> StmtId -> Either ParseError Stmt
+parseTemplateWithStmtId t tName sId = runParser parseStmtSeq (tName,sId) "" (T.concat [(T.pack "%]"), t, (T.pack "[%")])
+
+parseTemplate tName t = parseTemplateWithStmtId t tName (0,0,0)
+ src/Text/TemplateToolkitLang.hs view
@@ -0,0 +1,453 @@+{-|
+Module      : Text.TemplateToolkitLang
+Description : Template Toolkit language manual for Haskell
+Copyright   : (c) Dzianis Kabanau, 2018
+Maintainer  : kobargh@gmail.com
+
+Template Toolkit is a template processing system originally written in Perl by Andy Wardley.
+Below is the complete documentation of features implemented in Haskell version of the system. Original documentation is available <http://www.template-toolkit.org/docs/index.html here>, but be warned that not all features are realized in Haskell version.
+
+-}
+
+module Text.TemplateToolkitLang (
+    -- * Tags
+    -- $tags
+    
+    -- * Variables
+    -- $variables
+    
+    -- * Operators
+    -- $operators
+    
+    -- * Directives
+    -- $directives
+    
+    -- * Virtual methods
+    -- $vmethods
+    
+    -- * Filters
+    -- $filters
+    
+ ) where
+
+{-$tags
+
+Character sequences @__[% %]__@ are used to enclose directive tags within a template.
+
+@
+ Hello __[% user.name %]__!
+@
+
+If directive tag starts by @__[%-__@ then all spaces before the tag are stripped. With @__-%]__@ all spaces after the tag are removed.
+
+Multiple directives may be enclosed within one tag. In this case they must be separated by @__;__@
+
+Comments are started by @__#__@
+
+@
+ __[%__
+   __FOREACH__ __x__ = [0,15,100];
+     __x__;     /# Comments here/
+   __END__;
+ __%]__
+@
+-}
+
+{- $variables
+There are three types of variables implemented: SCALARS, ARRAYS and HASHES.
+
+Dots are used to traverse hashes and arrays: @__[% my_hash.key1 %]__@, @__[% my_array.0 %]__@.
+
+Arrays may be represented in range form: @__[% xs = [1..10] %]__@
+
+Actually arrays and hashes are kind-of Perl's arrayrefs and hashrefs.
+So, for example, if two variables refer to one hash, modifying one variable affects another too.
+Assignment to undefined hashes and arrays __autovivificates__ needed structures.
+
+@
+ __[%__
+   /# scalar/
+   __int__ = 100;
+   __flt__ = 0.2;
+   __str__ = \"Hello \\\"guest\\\"\"; /# backslash is used to escape characters \" \' \\ $ and to type newlines \\n and tabs \\t./
+   
+   /# array/
+   __arr__ = [1,2,\"three\"];
+   __rng__ = [1..10];       /# [1,2,3,4,5,6,7,8,9,10]/
+   
+   /# hash/
+   __foo__ = {
+     k1 => 1,
+     k2 => \"value 2\",
+     k3 => [1,2,3],
+     k4 => {
+        subk1 => 10,
+        subk2 => 20,
+     }
+   };
+   
+   __bar__ = __foo.k4;__
+   __bar.subk1__ = \"this is ten\";
+   __foo.k4.subk1;__        /# \'this is ten\'/
+   
+   __bazarray.10.k1__ = 1; /# autovivification of array \'bazarray\' and hash \'bazarray.10\'/
+   
+ __%]__
+@
+
+Variables may be __interpolated__ in double-quoted strings and in complex dotted variables (hashes or arrays).
+
+__$__ character is used to precede interpolated variable: @"__$var__"@. Complex variables may be enclosed in curly braces: @"__${myhash.k1.0}__"@. 
+
+Single-qouted strings do not interpolate variables.
+
+@
+ __[%__
+   __i__ = 2;
+   __kbar__ = \'bar\';
+   __arr__ = [\'one\',\'two\',\'three\',\'four\',\'five\'];
+   __hsh__ = {foo => 10, bar => 20};
+   __arr.$i__; /# \'three\'/
+   \"Foo is __${hsh.foo}__.0\";         /# \'Foo is 10.0\'/
+   __hsh.$kbar__; /# 20/
+ __%]__
+@
+-}
+
+{-$operators
+=== Arithmetic operators
+
+@
+ __[%__
+   15 + 3.14;    /# 18.14/
+   12 - 10;      /# 2/
+   2 * 2;        /# 4/
+   10 \/ 4;       /# 2.5/
+
+   5 % 2;        /# 1       modulus (remainder) of division/
+                 /#         \'mod\' - synonym of \'%\'/
+   13 mod 5;     /# 3/
+
+   +7;           /# 7       unary positive/
+   -7;           /# -7      unary negative/
+ __%]__
+@
+
+=== Logical operators
+__0__ and empty strings are /false/ values. Everything else is /true/. Internal representation of /true\/false/ is __1__\/__0__ respectively.
+
+If both operands of comparison are strings, then they are compared as strings. Otherwise string operand is parsed as number.
+
+@
+ __[%__
+   100 __>__ 99;            /# 1        true/
+   100 __>=__ 101;          /# 0        false/
+   100 __<__ 100;           /# 0        false/
+   100 __<=__ 100;          /# 1        true/
+   100 __==__ 100;          /# 1        true/
+   100 __!=__ 100;          /# 0        false/
+   __!__0;                  /# 1        unary negation/
+   
+   \"100\" > 2;           /# 1        true - numeric comparison/
+   \"100\" > \"2\";         /# 0        false - string comparison/
+   
+   0 __||__ 100;            /# 100/
+                        /# \'or\' - synonym of \'||\'/
+   30 __or__ \'\';            /# 30/
+   
+   \'fox\' __&&__ \'dog\';      /# \'dog\'/
+                        /# \'and\' - synonym of \'&&\'/
+   \'fox\' __and__ \"\";        /# 0        evaluates to false value/
+   
+   /# operations group with ( )/
+   (\"string\" && 0) __?__ \"snow\" __:__ \"rain\";      /# \'rain\'/
+   /# cond ? val_true : val_false          ternary condition operator/
+   /# complex operands of ternary condition operator must be enclosed in parentheses:/
+   (!loop.last) ? \'not last\' : \'last\' /# \'not last\' or \'last\'/
+   !loop.last ? \'not last\' : \'last\' /# always evals to 0 - same as !(loop.last ? \'not last\' : \'last\')/
+ __%]__
+@
+
+=== String concatenation
+Underscore @__\___@ separated by spaces is used to concatenate strings.
+
+@
+ __[%__
+   __name__ = \"Zork\";
+   \"Hello,\" _ __name__;        /# \'Hello, Zork\'/
+ __%]__
+@
+-}
+
+{-$directives
+=== Conditionals
+
+__IF__ ... [ELSIF...] [ELSE...] __END__
+
+@
+__[% IF user.name__ == \'root\' __%]__
+
+Hello, Admin!
+
+__[% ELSIF user.status__ > 1 __%]__
+
+Welcome, master __[% user.name %]__!
+
+__[% ELSE %]__
+
+Hi, __[% user.name %]__!
+
+__[% END %]__
+@
+
+=== Loops
+
+__FOREACH__ x = ... __END__
+
+Special variable @__loop__@ is used to save information about foreach loop and current iteration.
+
+@
+__[%__ __FOREACH__ __i__ = [10..15] __%]__
+
+ I is __[% i %]__
+
+ __[%__
+   __IF__ __i__ == 12;
+     __loop.index;__ /# 2       current index/
+     __loop.count;__ /# 3       iteration count - index+1/
+     __loop.first;__ /# 10      first loop element/
+     __loop.last;__  /# 15      last loop element/
+     __loop.prev;__  /# 11      previous loop element/
+     __loop.next;__  /# 13      next loop element/
+     __loop.odd;__   /# 1       true if iteration count is odd/
+     __loop.even;__  /# 0       true if iteration count is even/
+   __END;__
+ __%]__
+
+__[% END %]__
+@
+
+__WHILE__ ... __END__
+
+@
+__[%__
+  x = 1;
+  __WHILE__ x <= 20;
+
+    "| X$x ";
+    x = x + 1; __IF__ x > 3; __LAST__; __END__;
+
+    __FOREACH__ y = [1..10];
+      __IF__ y > 2 && y < 9; __NEXT__; __END__;
+      "y$y ";
+    __END__;
+    
+  __END__;
+  /# Output: | X1 y1 y2 y9 y10 | X2 y1 y2 y9 y10 | X3 y1 y2 y9 y10/
+__%]__
+@
+
+__LAST__ is used to immediately break current loop. __NEXT__ - to skip to the next iteration.
+
+=== Processing template files and blocks
+
+__PROCESS__ /blockname_or_filename/ [var1 = ... var2 = ... varN = ...]
+
+__BLOCK__ /blockname/ ... __END__
+
+@
+\<html>
+  __[%__ __PROCESS__ header.tt __title__ = \'Homepage\' __descr__ = \'Homepage description\' __keyw__ = \'homepage,home,firstpage\' __%]__
+
+  \<p>Hello, __[% user.name %]__!
+  
+  __[% BLOCK__ foobarbazzer __%]__
+    \<p>Foo__[% word %]__barbaz
+  __[% END %]__
+  
+  __[%__
+    __FOREACH w__ = [\'fox\', \'dog\', \'cat\'];
+      __PROCESS__ foobarbazzer __word__ = __w__; /# \<p>Foofoxbarbaz \<p>Foodogbarbaz \<p>Foocatbarbaz/
+    __END__;
+  __%]__
+  
+  __[%__ __PROCESS__ footer.tt __%]__
+\</html>
+@
+
+Variable may be passed to __PROCESS__ or __WRAPPER__ directive as a name using __$__: @__[% PROCESS $template %]__@
+
+=== Wrapper
+
+__WRAPPER__ /wrapper_block_or_filename/ [var1 = ... var2 = ... varN = ...] ... __END__
+
+The @__WRAPPER__@ block evaluates its content and passes the result in the variable @__content__@ to /wrapper_block_or_filename/. This is very useful for web templates with the same skeleton.
+
+@
+/FILE index.tt/
+__[% WRAPPER html_wrap.tt title = \'Barbaz homepage!\' %]__
+\<p>Welcome to Barbaz home!
+__[% END %]__
+
+
+/FILE html_wrap.tt/
+\<html>
+\<head>
+ \<meta http-equiv=\"content-type\" content=\"text/html; charset=utf-8\">
+ \<title>__[% title %]__\</title>
+\</head>
+\<body>
+__[% content %]__
+\</body>
+\</html>
+@
+
+-}
+
+{-$vmethods
+
+Virtual methods are applied to the variables to get new values or to modify array or hash variable.
+
+@__[% my_array.size %]__@ - Get the number of elements in @__my_array__@.
+
+@__[% karr = my_hash.keys %]__@ - Create array @__karr__@. The elements are the keys of @__my_hash__@.
+
+@__[% my_array.push(\"Baz\") %]__@ - Modify array @__my_array__@ appending scalar @__\"Baz\"__@ to the end of the array.
+
+Methods that use regular expressions are based on __pcre__ patterns, e.g. @__[% bar.match(\'(?i)^baz\\\\d$\') %]__@.
+
+Below is the list of implemented virtual methods. You can also take a look at the original documentation: <http://www.template-toolkit.org/docs/manual/VMethods.html Template Toolkit VMethods>.
+
+===Scalar vmethods
+[@.collapse@] Returns the string with trailing and leading spaces removed and internal multiple spaces converted to single space.
+
+[@.defined@] Returns true if the value is defined.
+
+[@.lcfirst@] Returns the string with first character converted to lower case.
+
+[@.length@] Returns the length of the string.
+
+[@.lower@] Returns the string converted to lower case.
+
+[@.match(/pattern/, /global/)@] Returns the array of captured substrings from regexing target string with @/pattern/@. If pattern-match fails, returns @/false/@. Optional @/global/@ is used for global pattern-match to return all captured substrings.
+
+[@.replace(/old/, /new/)@] Returns the string with all occurrences of @/old/@ replaced by @/new/@.
+
+[@.split(/pattern/)@] Returns the array produced by splitting the string by @/pattern/@. 
+
+[@.trim@] Returns the string with leading and trailing spaces removed.
+
+[@.ucfirst@] Returns the string with first character converted to upper case.
+
+[@.upper@] Returns the string converted to upper case.
+
+===Array vmethods
+
+[@.first@] Returns the first element of the array.
+
+[@.grep(/pattern/)@] Returns the array of elements that match @/pattern/@.
+
+[@.import(/arr1/, /arr2/, /arrN/)@] Imports to the target array elements of all passed arrays. Target array is modified.
+
+[@.join(/separator/)@] Returns the string created by joining all elements of the array separated by @/separator/@.
+
+[@.last@] Returns the last element of the array.
+
+[@.nsort@] Returns the array sorted numerically.
+
+[@.pop@] Removes the last element from the array. Target array is modified.
+
+[@.push(/el1/, /el2/, /elN/)@] Appends element(s) to the end of the array. Target array is modified.
+
+[@.reverse@] Returns reversed version of the array.
+
+[@.shift@] Removes the first element from the array. Target array is modified.
+
+[@.size@] Returns the number of elements in the array.
+
+[@.slice(/from/, /to/)@] Returns the array of elements idexing in the range @/from/../to/@ of the original array. If @/to/@ is omitted, then to the last element.
+
+[@.sort@] Returns the array sorted alphabetically.
+
+[@.splice(/offset/,/length/,/insertarray/)@] Powerful array-modification method that behaves just like <https://perldoc.perl.org/functions/splice.html Perl's splice>. Removes from the array @/length/@ elements starting from @/offset/@ index, replacing them with @/insertarray/@. If @/insertarray/@ is omitted - just removes elements. If @/length/@ is omitted removes everything up to the end of the array. @/length/@ may be @0@ - no elements are removed. @/offset/@ may be negative - counts index from the end of the array, e.g. @-2@ - penultimate element's index. Target array is modified.
+
+[@.unique@] Returns the array of only unique elements.
+
+[@.unshift(/el1/, /el2/, /elN/)@] Prepends element(s) to the beginning of the array. Target array is modified.
+
+===Hash vmethods
+
+[@.delete(/key1/, /key2/, /keyN/)@] Deletes items identified by the keys from the hash. Target hash is modified.
+
+[@.each@] Returns keys and values of the hash in one array - @[key1, val1, key2, val2, ... keyN, valN]@.
+
+[@.keys@] Returns the array of the hash keys.
+
+[@.import(/hash1/, /hash2/, /hashN/)@] Imports to the target hash items of all passed hashes. Existing items get new values, new items are added. Target hash is modified.
+
+[@.item(/key/)@] Returns the value of hash item identified by /key/. This is useful to get values with keys that contain any characters. E.g. @__[% hash.item('complex $ key.') %]__@ or @__[% hash.item('12') %]__@.
+
+[@.pairs@] Returns the array of key-value pairs of the hash. The pairs are hashes each with two keys: @__key__@ and @__value__@.
+
+[@.size@] Returns the number of items in the hash.
+
+[@.values@] Returns the array of the hash values.
+
+Sometimes a hash may have items whith keys that conflict with the above method names. In this situation parentheses may be used to force method call.
+
+@
+__[%__
+  __my_car__ = {
+    \'keys\' => \'lost\',
+    \'mileage\' => 150000,
+  };
+  __my_car.keys__; /# \'lost\'/
+  __my_car.keys()__; /# [\'keys\', \'mileage\']/
+__%]__
+@
+
+-}
+
+{-$filters
+
+Filters transform text of a block. Filter may be applied as a directive:
+
+__FILTER__ /filter_name/ ... __END__
+
+Or in piped form (multiple filters may be chained):
+
+/expr/ __|__ /filter_name [| /filter_name]/
+
+@
+__[% FILTER collapse %]__
+__[% PROCESS body.tt %]__
+__[% END %]__
+
+The same:
+
+__[% PROCESS body.tt | collapse %]__
+@
+
+Below is the list of implemented filters.
+
+[@collapse@] Removes leading and trailing spaces. Internal multiple spaces are converted to single space.
+
+[@html@] Replaces all @__\<__@, @__>__@, @__&__@, @__\"__@ with @__&lt;__@, @__&gt;__@, @__&amp;__@, @__&quot;__@ respectively.
+
+[@lcfirst@] Converts the first character to lower case.
+
+[@lcfirst@] Converts all characters to lower case.
+
+[@null@] Discards the text of block
+
+[@replace(/old/, /new/)@] Replaces all occurrences of @/old/@ with @/new/@.
+
+[@trim@] Removes leading and trailing spaces.
+
+[@ucfirst@] Converts the first character to upper case.
+
+[@upper@] Converts all characters to upper case.
+
+[@uri@] URI-encodes text.
+
+-}
template-toolkit.cabal view
@@ -1,33 +1,52 @@--- Initial template-toolkit.cabal generated by cabal init.  For further 
--- documentation, see http://haskell.org/cabal/users-guide/
-
-name:                template-toolkit
-version:             0.1.0.1
-synopsis:            Template Toolkit implementation for Haskell
-description:         Template Toolkit is a template processing system originally written in Perl by Andy Wardley.
-					 This haskell implementation includes such features as:
-					 scalar, array, hash variables, variable interpolation,
-					 conditional directives,
-					 loops and loop controls,
-					 external templates and internal blocks processing,
-					 lots of virtual methods and filters.
-					 
-homepage:            https://github.com/kobargh/template-toolkit
-license:             GPL-3
-license-file:        LICENSE
-author:              Dzianis Kabanau
-maintainer:          kobargh@gmail.com
--- copyright:           
-category:            Text
-build-type:          Simple
-extra-source-files:  example/*.hs, example/*.json, example/*.tt
-cabal-version:       >=1.10
-
-library
-  exposed-modules:     Text.TemplateToolkit, Text.TemplateToolkitLang
-  other-modules:       Text.TemplateToolkitAST
-  -- other-extensions:    
-  -- build-depends:       base >=4.8 && <4.9, parsec >=3.1 && <3.2, text >=1.2 && <1.3, containers >=0.5 && <0.6, mtl >=2.2 && <2.3, directory >=1.2 && <1.3, unordered-containers >=0.2 && <0.3, bytestring >=0.10 && <0.11, regex-pcre-builtin >=0.94 && <0.95, pcre-utils >=0.1 && <0.2, uri-encode >=1.5 && <1.6, aeson >=1.2 && <1.3, scientific >=0.3 && <0.4
-  build-depends:       base >=4.7 && <4.10, parsec, text, containers, mtl, directory, unordered-containers, bytestring, regex-pcre-builtin, pcre-utils, uri-encode, aeson, scientific
-  -- hs-source-dirs:      
-  default-language:    Haskell2010+cabal-version: >=1.10+name: template-toolkit+version: 0.1.1.0+license: GPL-3+license-file: LICENSE+copyright: 2018 Dzianis Kabanau+maintainer: kobargh@gmail.com+author: Dzianis Kabanau+homepage: https://github.com/kobargh/template-toolkit#readme+bug-reports: https://github.com/kobargh/template-toolkit/issues+synopsis: Template Toolkit implementation for Haskell+description:+    Template Toolkit is a template processing system originally written in Perl by Andy Wardley.+    This haskell implementation includes such features as:+    scalar, array, hash variables, variable interpolation,+    conditional directives,+    loops and loop controls,+    external templates and internal blocks processing,+    many virtual methods and filters,+    templates caching.+category: Text+build-type: Simple+extra-source-files:+    ./example/*.hs+    ./example/*.json+    ./example/*.tt+    ChangeLog.md++library+    exposed-modules:+        Text.TemplateToolkit+        Text.TemplateToolkitAST+        Text.TemplateToolkitLang+    hs-source-dirs: src+    default-language: Haskell2010+    build-depends:+        aeson >=1.3.1.1,+        base >=4.7 && <5,+        binary >=0.8.5.1,+        bytestring >=0.10.8.2,+        containers >=0.5.11.0,+        directory >=1.3.1.5,+        hashtables >=1.2.3.1,+        mtl >=2.2.2,+        parsec >=3.1.13.0,+        pcre-utils >=0.1.8.1.1,+        regex-pcre-builtin >=0.94.4.8.8.35,+        scientific >=0.3.6.2,+        text >=1.2.3.1,+        time >=1.8.0.2,+        unordered-containers >=0.2.9.0,+        uri-encode >=1.5.0.5