人気ブログランキング | 話題のタグを見る

Parsec 字句解析プログラムジェネレータ

前回の expr..hs では、数値と演算子の間にスペースが入るときちんと計算できなかった。余分なスペースをとったり、コメントを外したりする作業は一般には字句解析プログラム(lexical analizer) が受け持つ。Parsec にはこの字句解析プログラムを作る関数 makeTokenParser がある。makeTokenParser は、Text.ParserCombinators.Parsec.Token モジュールのなかにある。

makeTokenParser は引数に言語の様式をまとめたもの(LanguageDef)を引数にとり、パーサ関数を集めたレコードを返す。DefaultDef は Text.ParserCombinators.Parsec.Language のなかに記述されている。下の例では、Haskell タイプのコメントを処理できる haskellDef を用いている。

このあたりまで来ると管理人の知識も怪しくなってくるので、詳しくはParsec, a fast combinator parser を読んだ方がいいかもしれない。ただ、そのままでは動かなかったので動作確認した例を次に示す。モジュールの命令に forall が使われているので、-98 オプションで起動しないと動かない。Parsec を本格的に使うのなら GHC を使うのが王道だろう。何回も終了宣言をした Haskell だが、動脈のシミュレーションもやったし、Parsec の概要もわかったのでしばらく休憩(になると思う)。

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

lexer :: P.TokenParser ()
lexer  = P.makeTokenParser
         (haskellDef
         { reservedOpNames = ["*","/","+","-"]
         })

whiteSpace= P.whiteSpace lexer
lexeme    = P.lexeme lexer
symbol    = P.symbol lexer
natural   = P.natural lexer
parens    = P.parens lexer
semi      = P.semi lexer
identifier= P.identifier lexer
reserved  = P.reserved lexer
reservedOp= P.reservedOp lexer

runLex :: Show a => Parser a -> String -> IO ()
runLex p input
        = parseTest (do{ whiteSpace
                    ; x <- p
                    ; eof
                    ; return x
                    }) input

expr :: Parser Integer
expr = buildExpressionParser table factor
      <?> "expression"

table = [[op "*" (*) AssocLeft, op "/" div AssocLeft]
        ,[op "+" (+) AssocLeft, op "-" (-) AssocLeft]
        ]
      where
        op s f assoc
          = Infix (do{ reservedOp s; return f}) assoc

factor = parens expr
        <|> natural
        <?> "simple expression"

実行例:
$ hugs -98 -Evi
__ __ __ __ ____ ___ _________________________________________
|| || || || || || ||__ Hugs 98: Based on the Haskell 98 standard
||___|| ||__|| ||__|| __|| Copyright (c) 1994-2005
||---|| ___|| World Wide Web: http://haskell.org/hugs
|| || Bugs: http://hackage.haskell.org/trac/hugs
|| || Version: September 2006 _________________________________________

Hugs mode: Restart with command line option +98 for Haskell 98 mode

Type :? for help
Hugs> :e expr.hs
Hugs> :l expr.hs
Main> runLex expr " 1 + (2 * 3) "
7
by tnomura9 | 2009-10-01 08:24 | Haskell | Comments(0)
<< フィンランドの教育革命 Parsec 式の定義 bui... >>