Hoe kan ik buildexpressionParser van Text.Parsec.expr gebruiken om deze taal te ontleden?

Ik heb geprobeerd buildExpressionParser te gebruiken om een ​​taal te ontleden, en ik heb het bijna. Dankzij Parsec.Expr herhaalde Prefix/Postfix-operator niet ondersteund voor het oplossen van een van mijn grote problemen.

Dit codefragment illustreert (wat ik hoop te zijn) mijn laatste probleem:

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]]

Dit parseert met succes (en correct) , 0 , .. 0 , ., 0 , .0 * 0 en , 0 * 0 ; het kan echter , 0 of .0 * .0 niet ontleden. Ik kan zien waarom die twee niet analyseren, maar ik zie niet hoe ik de parser kan wijzigen zodat geen van de successen verandert en de twee fouten worden geanalyseerd.

One way of "solving" this would be to change (fmap Lit digit) to (fmap Lit Digit <|> expr), but then the parser would loop instead of erroring.

Advies welkom.

EDIT: De volgende parsen zijn de sleutel:

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

> parseTest expr ",0*0"
B (A1 (Lit '0')) (Lit '0')
4
ja ru de

1 antwoord

Te krijgen '.' en ',' op een niveau dat je ze samen zou kunnen behandelen:

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'))

Bewerken, dit was de eerdere kennelijk ontoereikende poging

 table =
   [  [prefix $ (char ',' >> return A1) <|> (char '.' >> return A2)]
   , [Infix   (char '*' >> return B) AssocNone]
   ]
4
toegevoegd
Ik heb het vervangen door een iets andere; het lijkt een beetje een hack.
toegevoegd de auteur applicative, de bron
Dit verandert de betekenis van , 0 * 0 of .0 * 0 , afhankelijk van de richting waarin u ze verplaatst.
toegevoegd de auteur Alex R, de bron