% -*- LaTeX -*-
% $Id: Unlit.lhs,v 1.2 2002/10/01 06:55:50 lux Exp $
%
% $Log: Unlit.lhs,v $
% Revision 1.2 2002/10/01 06:55:50 lux
% unlit returns an error message to the caller instead of calling error.
%
% Revision 1.1 2000/02/07 14:05:55 lux
% The compiler now supports literate source files. Literate source files
% must end with the suffix ".lcurry".
%
%
\nwfilename{Unlit.lhs}
\section{Literate comments}
Since version 0.7 of the language report, Curry accepts literate
source programs. In a literate source all program lines must begin
with a greater sign in the first column. All other lines are assumed
to be documentation. In order to avoid some common errors with
literate programs, Curry requires at least one program line to be
present in the file. In addition, every block of program code must be
preceded by a blank line and followed by a blank line.
The module \texttt{Unlit} acts as a preprocessor which converts
literate source programs into the ``un-literate'' format accepted by
the lexer. The implementation, together with the comments below, was
derived from appendix D in the Haskell 1.2 report.
\begin{verbatim}
> module Unlit(unlit) where
> import Data.Char
> import Position
\end{verbatim}
Each of the lines in a literate script is a program line, a blank
line, or a comment line. In the first case the text is kept with the
line.
\begin{verbatim}
> data Classified = Program String | Blank | Comment
\end{verbatim}
In a literate program, program lines begin with a \verb|>| character,
blank lines contain only whitespace, and all other lines are comment
lines.
\begin{verbatim}
> classify :: String -> Classified
> classify "" = Blank
> classify (c:cs)
> | c == '>' = Program cs
> | all isSpace (c:cs) = Blank
> | otherwise = Comment
\end{verbatim}
In the corresponding program, program lines have the leading \verb|>|
replaced by a leading space, to preserve tab alignments.
\begin{verbatim}
> unclassify :: Classified -> String
> unclassify (Program cs) = ' ' : cs
> unclassify Blank = ""
> unclassify Comment = ""
\end{verbatim}
Process a literate program into error messages (if any) and the
corresponding non-literate program.
\begin{verbatim}
> unlit :: FilePath -> String -> (String,String)
> unlit fn lcy = (es,cy)
> where cs = map classify (lines lcy)
> es = unlines (errors fn cs)
> cy = unlines (map unclassify cs)
\end{verbatim}
Check that each program line is not adjacent to a comment line and
there is at least one program line.
\begin{verbatim}
> errors :: FilePath -> [Classified] -> [String]
> errors fn cs =
> concat (zipWith3 adjacent (iterate nl (first fn)) cs (tail cs)) ++
> empty fn (filter isProgram cs)
\end{verbatim}
Given a line number and a pair of adjacent lines, generate a list of
error messages, which will contain either one entry or none.
\begin{verbatim}
> adjacent :: Position -> Classified -> Classified -> [String]
> adjacent p (Program _) Comment = [message (nl p) "after"]
> adjacent p Comment (Program _) = [message p "before"]
> adjacent p _ _ = []
> message p w = show p ++ ": comment line " ++ w ++ " program line."
\end{verbatim}
Given the list of program lines generate an error if this list is
empty.
\begin{verbatim}
> empty :: FilePath -> [Classified] -> [String]
> empty fn [] = [show (first fn) ++ ": no code in literate script"]
> empty fn _ = []
> isProgram :: Classified -> Bool
> isProgram (Program _) = True
> isProgram _ = False
\end{verbatim}