This program is written in Literate Haskell.
All lines starting with '> ' are Haskell, others are comments.

Haskell is a lazy functional and pure language.

This program is not very optimized... it just work.

This program should be used as pipe:
 - STDIN: template to check
 - STDOUT:
    - nothing: no problem found
    - numbers separated by space : lines number where DIRECTIVE are found in TAG
    - an error message in parentheses (generally when EOF in found inner not toplevel context)


First, the haskell module definition (mandatory).

> module Main where
 
Next, we create the entrypoint of the program: the 'main' funtion.

The function use 'interact' for interfacing with STDIN/STDOUT.
The argument of 'interact' is a function which take a list of Char (type String) and return a list of Char.

We start in the INITIAL context, with line number set to 1

> main :: IO ()
> main = interact $ inINITIALContext 1

All the context have the form:
 an Integer argument : the line number position
 a list of Char (the String) : the current position of STDIN

and return
 a list of Char (type String) : STDOUT output


Functions are composed of:
 - function signature (not mandatory [else infered], but good practice)
 - the body, using Pattern Matching

In the INITIAL context:
 - increment 'line' on newline
 - if start with '<!--', go in COMMENT context
 - if start with '<![CDATA[', go in CDATA context
 - if start with '<', go in TAG context
 - else, discard char
 - EOF in INITIAL: so end

> inINITIALContext :: Integer -> String -> String

> inINITIALContext line ('\n':xs) = inINITIALContext (line+1) xs
> inINITIALContext line ('<':'!':'-':'-':xs) = inCOMMENTContext line xs
> inINITIALContext line ('<':'!':'[':'C':'D':'A':'T':'A':'[':xs) = inCDATAContext line xs
> inINITIALContext line ('<':xs)  = inTAGContext line xs
> inINITIALContext line (_x:xs)   = inINITIALContext line xs
> inINITIALContext _line [] = ""


In the COMMENT context:
 - increment 'line' on newline
 - go in INITIAL at end of comment '-->'
 - discard char else
 - EOF in COMMENT

> inCOMMENTContext :: Integer -> String -> String

> inCOMMENTContext line ('\n':xs)        = inCOMMENTContext (line+1) xs
> inCOMMENTContext line ('-':'-':'>':xs) = inINITIALContext line xs
> inCOMMENTContext line (_x:xs) = inCOMMENTContext line xs
> inCOMMENTContext _line []     = "(EOF in COMMENT)"

In the CDATA context:
 - increment 'line' on newline
 - go in INITIAL at end of cdata ']]>'
 - discard char else
 - EOF in CDATA


> inCDATAContext :: Integer -> String -> String

> inCDATAContext line ('\n':xs) = inCDATAContext (line+1) xs
> inCDATAContext line (']':']':'>':xs) = inINITIALContext line xs
> inCDATAContext line (_xs:xs)  = inCDATAContext line xs
> inCDATAContext _line []       = "(EOF in CDATA)"


In the TAG context:
 - increment 'line' on newline
 - go in STRING if '"' found
 - go back in INITIAL if '>'
 - found a DIRECTIVE TEMPLATE in TAG if '[%': output the line (with conversion Integer -> String using 'show'), append space, append the rest (keep in TAG context, continue parsing)
 - EOF in TAG

> inTAGContext :: Integer -> String -> String

> inTAGContext line ('\n':xs)    = inTAGContext (line+1) xs
> inTAGContext line ('"':xs)     = inSTRINGContext line xs
> inTAGContext line ('>':xs)     = inINITIALContext line xs
> inTAGContext line ('[':'%':xs) = show line ++ " " ++ inTAGContext line xs
> inTAGContext line (_x:xs)      = inTAGContext line xs
> inTAGContext _line []          = "(EOF in TAG)"


In the STRING context:
 - increment 'line' on newline
 - skip \" (keep STRING context)
 - go in TAG context at end of string
 - discard else
 - EOF in STRING context

> inSTRINGContext :: Integer -> String -> String

> inSTRINGContext line ('\n':xs)     = inSTRINGContext (line+1) xs
> inSTRINGContext line ('\\':'"':xs) = inSTRINGContext line xs
> inSTRINGContext line ('"':xs)      = inTAGContext line xs
> inSTRINGContext line (_x:xs)       = inSTRINGContext line xs
> inSTRINGContext _line []           = "(EOF in STRING)"

