This is a multi-part message in MIME format.
--F93F7E72348E2F23CC7D1D40
Content-Type: text/plain; charset=us-ascii
Content-Transfer-Encoding: 7bit
Manuel says
One of our students just pointed out an IMHO rather problematic clause in the
layout rule ... So, I guess (I hope!!) there is a nifty trick that lets you
achieve the same effect by using only conditions depending on local
information ...
Attached is Haskell code which handles the layout rule reasonably well as a
separate pass between scanning and parsing (though it is Haskell 1.4 rather
than 98 and imperfect).
--
Ian[EMAIL PROTECTED], http://www.cs.bris.ac.uk/~ian
--F93F7E72348E2F23CC7D1D40
Content-Type: text/plain; charset=us-ascii; name="Layout.hs"
Content-Transfer-Encoding: 7bit
Content-Disposition: inline; filename="Layout.hs"
{--
LAYOUT ANALYSIS
The layout function deals with the layout conventions of Haskell 1.4, inserting
extra "{", ";" and "}" tokens to represent implicit blocks. The inserted
tokens are marked as implicit, and are inserted as early as possible in the
token stream, in order to promote well-phrased and well-positioned error
messages in case of trouble. The layout function never fails; it is left up to
a parser to detect errors.
The Haskell standard says that a block (or layout list) is terminated "whenever
the syntactic category containing the layout list ends, that is, if an illegal
lexeme is encountered at a point where a close brace would be legal". This can
only be implememented easily if layout processing is combined with parsing.
Here, layout processing is done separately, so an approximation to the standard
is achieved by keeping track of brackets. See the end of this file for
examples where the layout function deviates from the standard.
ISSUES TO BE RESOLVED
-- "case" terminated by "where" may be common enough to make a special case
-- "case" terminated by "," may be worth dealing with
-- check that "let"s inside "do" (which don't have "in") are handled OK
-- check explicit blocks, and their interaction with implicit ones
Ian Holyer @(#) $Id: Layout.hs,v 1.2 1998/10/26 15:18:39 ian Exp $
--}
module Layout (layout) where
import Haskell
import Lex
-- Start layout processing. If the source does not begin with "module" or "{",
-- then there is an implicit surrounding block. Here and elsewhere, a
-- lookahead past possible comments is done so that a token can be inserted
-- before the comments if necessary; also, the end-of-file token makes it
-- unnecessary to check for an empty token stream.
layout :: [Token] - [Token]
layout ts =
if s == "module" then comments ++ scanExplicit [] (tok:rest) else
openBlock [] (Tok "}" 1 0 Implicit) ts
where
comments = takeWhile isComment ts
tok @ (Tok s r c k) : rest = dropWhile isComment ts
-- A stack of tokens is used to keep track of the surrounding blocks. For each
-- block, its opening "{" token is pushed onto the stack. In an implicit
-- block, the brackets "(",")" and "[","]" and "case","of" and "let","in" and
-- "if","then","else" are tracked by putting the opening bracket on the stack
-- until the matching closing bracket is found. Each opening bracket is stored
-- on the stack with the indent for the current block in place of its actual
-- column.
type Stack = [Token]
-- Scan the source tokens while in an explicit block (or while not in any
-- blocks) when layout is inactive. Look for an explicit close block token, or
-- a keyword which indicates the beginning of a new block. Treat field selector
-- as an explicit block.
scanExplicit :: Stack - [Token] - [Token]
scanExplicit stack [] = []
scanExplicit stack (t@(Tok s r c k) : ts1) = case s of
"}" - t : closeBlock stack t ts1
"where" - t : openBlock stack t ts1
"let" - t : openBlock stack t ts1
"do" - t : openBlock stack t ts1
"of" - t : openBlock stack t ts1
"{" - openBlock stack undefined (t:ts1)
_ - t : scanExplicit stack ts1
-- Scan the source tokens while in an implicit block, with layout active. The
-- parameters are the stack, the last token dealt with, and the remaining
-- tokens. The block is terminated by indenting or by a suitable closing
-- bracket. Treat field selector as an explicit block.
scanImplicit :: Stack - Token - [Token] - [Token]
scanImplicit stack@(Tok bs br bc bk : stack1) last@(Tok ls lr lc lk) ts =
if c bc || k == EndToken then close els