Parsec 入門


Parsec コンビネータ between

between open close p

between open close p コンビネータは open パターンと close パターンの間のパターンにマッチする。括弧の間の部分にマッチさせるのに便利だ。
Prelude Text.Parsec> parseTest (between (char '(') (char ')') (many letter))
 "(hello)"
"hello"

<* 演算子

括弧を使うときはスペースで間を開けることも多いが、上のパーサではそれは処理できない。しかし、次のように、全てのパーサのあとに、スペースをスキップする spaces パーサを <* 演算子でつなげると、うまくトークン間の空白を処理できる。

Prelude Text.Parsec> parseTest (between (char '(' <* spaces) (char ')' <*
 spaces) (many letter <* spaces)) "( hello ) "
"hello"

between と同じことは open *> p <* close でもできるが、これは between の定義と同じものだ。

Prelude Text.Parsec> parseTest (char '(' *> many letter <* char ')') "(hello)"
"hello"

makeTokenParser

トークンの後ろに <* spaces を使うテクニックはプログラム言語を記述するときに普遍的な要素になる。しかし、以前の記事で紹介した makeTokenParser を利用してパーサを自動生成する方法では、すでに空白処理はパーサに含まれているようだ。

Prelude> :l calculator
[1 of 1] Compiling Calculator ( calculator.hs, interpreted )
Ok, modules loaded: Calculator.
*Calculator> parseTest expr "(1 + 2) * (3 + 4)"
21

ファイル名:calculator.hs

module Calculator where

import Text.Parsec import qualified Text.Parsec.Token as P import Text.Parsec.Language

mytoken = P.makeTokenParser emptyDef symbol = P.symbol mytoken integer = P.integer mytoken parens = P.parens mytoken

expr = term `chainl1` addop term = factor `chainl1` mulop factor = parens expr <|> integer

mulop = (symbol "*" >> return (*)) <|> (symbol "/" >> return (div)) addop = (symbol "+" >> return (+)) <|> (symbol "-" >> return (-))