2016-05-30 2 views
0

J'ai besoin pour analyser cette syntaxe pour la déclaration de fonctionEst-il possible de forcer le retour de toutes les options?

foo x = 1 
Func "foo" (Ident "x") = 1 

foo (x = 1) = 1 
Func "foo" (Label "x" 1) = 1 

foo x = y = 1 
Func "foo" (Ident "x") = (Label "y" 1) 

J'ai écrit cet analyseur

module SimpleParser where 
import Text.Parsec.String (Parser) 
import Text.Parsec.Language (emptyDef) 
import Text.Parsec 
import qualified Text.Parsec.Token as Tok 
import Text.Parsec.Char 
import Prelude 

lexer :: Tok.TokenParser() 
lexer = Tok.makeTokenParser style 
    where 
    style = emptyDef { 
       Tok.identLetter = alphaNum 
      } 

parens :: Parser a -> Parser a 
parens = Tok.parens lexer 

commaSep :: Parser a -> Parser [a] 
commaSep = Tok.commaSep1 lexer 

commaSep1 :: Parser a -> Parser [a] 
commaSep1 = Tok.commaSep1 lexer 


identifier :: Parser String 
identifier = Tok.identifier lexer 

reservedOp :: String -> Parser() 
reservedOp = Tok.reservedOp lexer 

data Expr = IntLit Int | Ident String | Label String Expr | Func String Expr Expr | ExprList [Expr] deriving (Eq, Ord, Show) 


integer :: Parser Integer 
integer = Tok.integer lexer 

litInt :: Parser Expr 
litInt = do 
    n <- integer 
    return $ IntLit (fromInteger n) 

ident :: Parser Expr 
ident = Ident <$> identifier 

paramLabelItem = litInt <|> paramLabel 

paramLabel :: Parser Expr 
paramLabel = do 
    lbl <- try (identifier <* reservedOp "=") 
    body <- paramLabelItem 
    return $ Label lbl body 

paramItem :: Parser Expr 
paramItem = parens paramRecord <|> litInt <|> try paramLabel <|> ident 

paramRecord :: Parser Expr 
paramRecord = ExprList <$> commaSep1 paramItem 

func :: Parser Expr 
func = do 
    name <- identifier 
    params <- paramRecord 
    reservedOp "=" 
    body <- paramRecord 
    return $ (Func name params body) 


parseExpr :: String -> Either ParseError Expr 
parseExpr s = parse func "" s 

Je peux analyser foo (x) = 1 mais je ne peux pas analyser foo x = 1

parseExpr "foo x = 1" 
Left (line 1, column 10): 
unexpected end of input 
expecting digit, "," or "=" 

Je comprends qu'il essaie analyser ce code comme Func "foo" (Label "x" 1) et échoue. Mais après échec pourquoi il ne peut pas essayer de l'analyser comme Func "foo" (Ident "x") = 1

Y at-il un moyen de le faire?

J'ai aussi essayé d'échanger ident et paramLabel

paramItem = parens paramRecord <|> litInt <|> try paramLabel <|> ident 
paramItem = parens paramRecord <|> litInt <|> try ident <|> paramLabel 

Dans ce cas, je peux analyser foo x = 1, mais je ne peux pas analyser foo (x = 1) = 2

parseExpr "foo (x = 1) = 2" 
Left (line 1, column 8): 
unexpected "=" 
expecting "," or ")" 

Répondre

1

Voici comment je comprends comment fonctionne parsec retours en arrière (et ne fonctionne pas):

Dans:

(try a <|> try b <|> c) 

si a échoue, b seront jugés, et si b échoue par la suite c seront jugés.

Cependant, dans:

(try a <|> try b <|> c) >> d 

si a réussit, mais d échoue, parsec ne pas revenir en arrière et essayer b. Une fois que a a réussi, Parsec considère l'ensemble du choix comme analysé et passe à d. Il ne reviendra jamais pour essayer b ou c.

Cela ne fonctionne pas non plus pour la même raison:

(try (try a <|> try b <|> c)) >> d 

Une fois a ou b réussit, l'ensemble réussit le choix et l'effet try externe réussit. L'analyse passe ensuite à d.

Une solution est de distribuer d à l'intérieur du choix:

try (a >> d) <|> try (b >> d) <|> (c >> d) 

Maintenant, si a réussit, mais d échoue, b >> d seront jugés.