Как я могу использовать buildExpressionParser из Text.Parsec.Expr для анализа этого языка?

Я пытался использовать buildExpressionParser для анализа языка, и у меня это почти получилось. Благодаря Parsec.Expr повторный оператор Prefix/Postfix не поддерживается за решение одной из моих больших проблем.

Этот фрагмент кода иллюстрирует (на что я надеюсь) мою последнюю трудность:

import Text.Parsec.Expr
import Text.Parsec

data Expr = Lit Char | A1 Expr | A2 Expr | B Expr Expr
  deriving (Show)

expr :: Parsec String () Expr
expr = buildExpressionParser table (fmap Lit digit)

prefix p = Prefix . chainl1 p $ return (.)

table =
  [ [prefix $ char ',' >> return A1]
  , [Infix   (char '*' >> return B) AssocNone]
  , [prefix $ char '.' >> return A2]]

Это успешно (и правильно) анализирует ,,0, ..0, .,0, .0*0 и ,0*0; однако он не может анализировать ,.0 или .0*.0. Я понимаю, почему эти два не анализируются, но я не понимаю, как я могу изменить анализатор, чтобы ни один из успехов не изменился, а два неудачных анализа.

Один из способов «решить» это — изменить (fmap Lit digit) на (fmap Lit Digit <|> expr), но тогда синтаксический анализатор зациклится, а не выдаст ошибку.

Совет приветствуется.

РЕДАКТИРОВАТЬ: Следующие синтаксические анализы являются ключевыми:

> parseTest expr ".0*0"
A2 (B (Lit '0') (Lit '0'))

> parseTest expr ",0*0"
B (A1 (Lit '0')) (Lit '0')

person Alex R    schedule 24.06.2012    source источник


Ответы (1)


Получить '.' и ',' на уровне, который вы могли бы рассматривать их вместе:

import Text.Parsec.Expr
import Text.Parsec

data Expr = Lit Char | A1 Expr | A2 Expr | B Expr Expr
  deriving (Show)

expr :: Parsec String () Expr
expr = buildExpressionParser table  (fmap Lit digit)
prefix p = Prefix . chainl1 p $ return (.)

table =
  [  [prefix $ (char ',' >> return A1) <|> (char '.' >> return A2)]
  , [Infix   (char '*' >> return B) AssocNone]
  , [prefix $ (char ',' >> return A1)] 
  ]

-- *Main> let f = parseTest expr
-- *Main> f ".,0"
-- A2 (A1 (Lit '0'))
-- *Main> f ".0*.0"
-- B (A2 (Lit '0')) (A2 (Lit '0'))
-- *Main> f ".0*,.0"
-- B (A2 (Lit '0')) (A1 (A2 (Lit '0')))
-- *Main> f ".,.0"
-- A2 (A1 (A2 (Lit '0')))
-- *Main> f ",.0"
-- A1 (A2 (Lit '0'))

Изменить, это была более ранняя явно неадекватная попытка

 table =
   [  [prefix $ (char ',' >> return A1) <|> (char '.' >> return A2)]
   , [Infix   (char '*' >> return B) AssocNone]
   ]
person applicative    schedule 24.06.2012
comment
Это изменяет значение ,0*0 или .0*0 в зависимости от того, в каком направлении вы их перемещаете. - person Alex R; 24.06.2012
comment
Я заменил его немного другим; это кажется чем-то вроде взлома. - person applicative; 24.06.2012