Grempa-0.1.0: examples/Ex2Calculator.hs
-- | Example 2: Parsing a list of tokens instead of a 'String' and computing
-- the desired result directly.
-- In this example it is assumed that there exists a lexer
-- that goes from @'String' -> 'CToken'@, so that an input
-- 'String' can be fed into the lexer and then into the generated
-- parser.
-- Needed for recursive do notation.
{-# LANGUAGE DoRec #-}
-- Needed for deriving 'Typeable'.
{-# LANGUAGE DeriveDataTypeable #-}
-- Needed for deriving 'Lift'.
{-# LANGUAGE TemplateHaskell #-}
module Ex2Calculator where
-- First import the Grempa grammar combinators.
import Data.Parser.Grempa.Grammar
-- We also need the 'ToPat' class to be in scope.
import Data.Parser.Grempa.Static (ToPat(..), toConstrPat)
-- The result datatype must be an instance of the 'Typeable' typeclass.
-- Fortunately, it is possible to derive an instance. Using the extension
-- above.
import Data.Typeable
import Data.Data
-- For deriving 'Lift' instances.
import Language.Haskell.TH.Lift
-- Our token datatype. The parser will operate on a list of those.
data CToken
= Num {unNum :: Integer}
| Plus
| Times
| LParen | RParen
-- Tokens have to have instances of a number of typeclasses ('Data', 'Eq',
-- 'Ord' and 'Show'). When making a static parser, they also have to be
-- members of 'Typeable' and also 'Lift' for 'toConstrPat' to work.
deriving (Data, Eq, Ord, Show, Typeable)
-- Derive a 'Lift' instance
$(deriveLift ''CToken)
-- The tokens of the language we are making a static parser for must have a
-- 'ToPat' instance, which provides a way for Grempa to convert the token
-- to a Template Haskell pattern matching. For tokens that should only be
-- compared on the constructor level, the implementation is easy, as there is
-- a function to do just that in Grempa.
instance ToPat CToken where
toPat = toConstrPat
-- | Our grammar operates on lists of 'CTokens' and returns the 'Integer'
-- result directly, without computing a tree-shaped result.
calc :: Grammar CToken Integer
-- This is very similar to the definition of the previous example, but using
-- operators operating on 'Integer's instead of constructors for the semantic
-- actions.
calc = do
rec
e <- rule [ (+) <@> e <# Plus <#> t
, id <@> t
]
t <- rule [ (*) <@> t <# Times <#> f
, id <@> f
]
f <- rule [ id <@ LParen <#> e <# RParen
, unNum <@> num
]
return e
where
-- We are using the fact that the parser will be able to only look at the
-- constructors when comparing different tokens if we want it to work that
-- way, which is why we can use for example this to represent any number
-- token.
num = Num 0