Alex Rivera | Logout

Parsing Indentation-based syntaxes in Haskell's Parsec

Asked 2010-06-11T14:10:15.747
14

I'm trying to parse an indentation-based language (think Python, Haskell itself, Boo, YAML) in Haskell using Parsec. I've seen the IndentParser library, and it looks like it's the perfect match, but what I can't figure out is how to make my TokenParser into an indentation parser. Here's the code I have so far:

import qualified Text.ParserCombinators.Parsec.Token as T
import qualified Text.ParserCombinators.Parsec.IndentParser.Token as IT

lexer = T.makeTokenParser mylangDef
ident = IT.identifier    lexer

This throws the error:

parser2.hs:29:28:
    Couldn't match expected type `IT.TokenParser st'
             against inferred type `T.GenTokenParser s u m'
    In the first argument of `IT.identifier', namely `lexer'
    In the expression: IT.identifier lexer
    In the definition of `ident': ident = IT.identifier lexer

What am I doing wrong? How should I create an IT.TokenParser? Or is IndentParser broken and to be avoided?

Edit
Report

1 Answer

6

Here is a set of parser combinators I put together for Parsec 3 that can be used for Haskell style layout, that might be of use to you. The key considerations are that laidout starts and runs a layout rule, and that you should use the space and spaced combinators provided rather than the stock Parsec combinators for the same purpose. Due to the interaction of layout and comments I had to merge the comment parsing into the tokenizer.

{-# LANGUAGE FlexibleContexts, FlexibleInstances, MultiParamTypeClasses #-}
module Text.Parsec.Layout
    ( laidout          -- repeat a parser in layout, separated by (virtual) semicolons
    , space            -- consumes one or more spaces, comments, and onside newlines in a layout rule
    , maybeFollowedBy
    , spaced           -- (`maybeFollowedBy` space)
    , LayoutEnv        -- type needed to describe parsers
    , defaultLayoutEnv -- a fresh layout
    , semi             -- semicolon or virtual semicolon
    ) where

import Control.Applicative ((<$>))
import Control.Monad (guard)

import Data.Char (isSpace)

import Text.Parsec.Combinator
import Text.Parsec.Pos
import Text.Parsec.Prim hiding (State)
import Text.Parsec.Char hiding (space)

data LayoutContext = NoLayout | Layout Int deriving (Eq,Ord,Show)

data LayoutEnv = Env
    { envLayout :: [LayoutContext]
    , envBol :: Bool -- if true, must run offside calculation
    }

defaultLayoutEnv :: LayoutEnv
defaultLayoutEnv = Env [] True

pushContext :: Stream s m c => LayoutContext -> ParsecT s LayoutEnv m ()
pushContext ctx = modifyState $ \env -> env { envLayout = ctx:envLayout env }

popContext :: Stream s m c => String -> ParsecT s LayoutEnv m ()
popContext loc = do
    (_:xs) <- envLayout <$> getState
    modifyState $ \env' -> env' { envLayout = xs }
  <|> unexpected ("empty context for " ++ loc)

getIndentation :: Stream s m c => ParsecT s LayoutEnv m Int
getI
answered 2010-06-11T14:30:26.220

Your Answer