- Parsec, 高速なコンビネータパーサ の写経の続きです。
import Text.ParserCombinators.Parsec hiding (spaces) import Text.ParserCombinators.Parsec.Expr run :: Show a => Parser a -> String -> IO () run p input = case (parse p "" input) of Left err -> putStr "parse error at " >> print err Right x -> print x 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{ string s; return f}) assoc factor = do{ char '(' ; x <- expr ; char ')' ; return x } <|> number <?> "simple expression" number :: Parser Integer number = do{ ds <- many1 digit ; return (read ds) } <?> "number"
*Main> run expr "1+2*3" --=> 7 *Main> run expr "(1+2)*3" --=> 9 *Main> run expr "8/4/2" --=> 1 *Main> run expr "8/(4/2)" --=> 4 *Main> run expr "1 + 2" --=> 1 *Main> run expr "1+ 2" parse error at (line 1, column 3): unexpected " " expecting simple expression *Main> run expr " 1+2" parse error at (line 1, column 1): unexpected " " expecting expression
テーブルの順序を変更すると計算の優先順位も変る。
table = [[op "+" (+) AssocLeft, op "-" (-) AssocLeft] ,[op "*" (*) AssocLeft, op "/" div AssocLeft]]
*Main> run expr "4+2*3" --=> 18 *Main> run expr "4+2/2" --=> 3
スペースを読みとばす。
spaces :: Parser () spaces = skipMany space number :: Parser Integer number = do{ spaces ; ds <- many1 digit ; spaces ; return (read ds) } <?> "number"
*Main> run expr "1 + 2" --=> 3 *Main> run expr "1 + 2 " --=> 3
buildExpressionParser 関数は二つの引数を取る。一つは演算の規則を定義した テーブルのリスト。もう一つは factor(要素) を定義したパーサ関数だ。演算の左結合、 右結合はテーブルに定義し、演算の優先度はテーブルのリスト内での前後関係で定義する。