Проблема с префиксным оператором Parke в Haskell

Я компилирую на Windows, используя GHC. Вот мой код ( также доступен здесь):

module GMC.GMLParser (parseGML) where

import Control.Applicative ((<$>), (<*>))
import Text.ParserCombinators.Parsec
import Text.ParserCombinators.Parsec.Expr
import Text.ParserCombinators.Parsec.Language
import qualified Text.ParserCombinators.Parsec.Token as P

type VarIdent = String
type FunIdent = String

data Expr =
      Var VarIdent
    | IntLit Integer
    | StringLit String
    | BiLit Bool
    | Op String Expr Expr
    | UnOp String Expr
    | Call FunIdent [Expr]
    deriving (Eq, Show)

data Stat =
      Seq [Stat]
    | Skip
    | Assign (Maybe VarIdent) Expr
    | If Expr Stat (Maybe Stat)
    deriving (Eq, Show)

lexer = P.makeTokenParser gmlDef

parens          = P.parens lexer    
braces          = P.braces lexer    
semi            = P.semi lexer
semiSep         = P.semiSep lexer  
semiSep1        = P.semiSep1 lexer    
commaSep        = P.commaSep lexer
commaSep1       = P.commaSep1 lexer
brackets        = P.brackets lexer
whiteSpace      = P.whiteSpace lexer    
symbol          = P.symbol lexer    
identifier      = P.identifier lexer    
reserved        = P.reserved lexer    
reservedOp      = P.reservedOp lexer
integer         = P.integer lexer    
charLiteral     = P.charLiteral lexer    
stringLiteral   = P.stringLiteral lexer


operators =
    [ [ prefix "-" ]
    , [ op "*"  AssocLeft, op "/"  AssocLeft ]
    , [ op "+"  AssocLeft, op "-"  AssocLeft ]
    , [ op "=" AssocNone, op "<>" AssocNone, op "<="  AssocNone
      , op "<" AssocNone, op ">="  AssocNone, op ">" AssocNone ]
    , [ op "&" AssocRight, op "&&" AssocRight ] -- Right for shortcircuiting
    , [ op "|" AssocRight, op "||" AssocRight ] -- Right for shortcircuiting
    , [ op ":=" AssocRight ]
    ]
    where
      op name assoc   = Infix (do{ reservedOp name
                                  ; return (\x y -> Op name x y) 
                                  }) assoc
      prefix name     = Prefix  (do{ reservedOp name
                                  ; return (\x -> UnOp name x)
                                  })


gmlDef :: LanguageDef st
gmlDef = emptyDef
    { commentStart    = "/*"
    , commentEnd      = "*/"
    , commentLine     = "//"
    , nestedComments  = True
    , identStart      = letter
    , identLetter     = alphaNum <|> oneOf "_"
    , reservedNames   = []
    , reservedOpNames = []
    , caseSensitive   = True
    }

parseGML :: String -> Either ParseError [Stat]
parseGML input = parse (whiteSpace >> many stat) "" input

intLit :: Parser Expr
intLit = IntLit <$> integer

strLit :: Parser Expr
strLit = StringLit <$> stringLiteral

variable :: Parser Expr
variable = do ident <- identifier
              memb <- optional $ symbol "." -- ignored for now, only parse its existance
              vname <- optional identifier -- ignored for now, only parse its existance
              indx <- optional $ brackets expr -- ignored for now, only parse its existance
              return (Var ident)

expr :: Parser Expr
expr = buildExpressionParser operators genExpr

genExpr :: Parser Expr
genExpr = choice [ intLit
                 , strLit
                 , try callExpr
                 , variable
                 , parens expr
                 ]

callExpr :: Parser Expr
callExpr = Call <$> identifier <*> parens (commaSep expr)

stat :: Parser Stat
stat =  do optional $ skipMany1 semi
           choice [ ifStat
                  , assignStat
                  , seqStat
                  ]

seqStat :: Parser Stat
seqStat = do stmts <- braces $ many stat
             return $ if null stmts then Skip else Seq stmts

ifStat :: Parser Stat
ifStat = If <$> (reserved "if" >> expr)
            <*> (optional (reserved "then") >> stat)
            <*> (optionMaybe $ reserved "else" >> stat)

assignStat :: Parser Stat
assignStat = do ident <- (optionMaybe $ try $ variable >> symbol "=")
                stmt <- case ident of
                    Just x -> expr
                    Nothing -> callExpr
                return (Assign ident stmt)

Проблема в том, что анализ префиксных вещественных чисел и переменных дает странные результаты.

x=-3 дает [Assign (Just "=") (UnOp "-" (IntLit 3))] что правильно. Более сложные выражения, такие как x=5+-3 а также x = (arr[4]>-1 && 1) однако, похоже, дают неверные результаты.

x = arr[4]>-1 дает [Assign (Just '=') (Var "arr")] однако должно быть [Assign (Just "x") (Op ">" (Var "arr") (UnOp "-" (IntLit 1)))]

x=5+-3 странно дает [Assign (Just "=" (IntLit 5)) когда это должно быть [Assign (Just "x") (Op "+" (IntLit 5) (UnOp "-" (IntLit 3)))]

Я думаю, это из-за того, что связано с моим приоритетом оператора, или, что в целом моя реализация префикса - Оператор кажется ненадежным. Я был бы очень признателен за руководство.

Спасибо!

2 ответа

Решение

Несколько вопросов:

ident <- (optionMaybe $ try $ variable >> symbol "=")

Это анализ и игнорирование variable, а затем возвращает результат symbol "=", Более того, variable будет ошибка типа здесь. Я буду использовать identifier вместо этого для тестирования здесь, но вы, вероятно, хотите что-то более необычное.

parse (whiteSpace >> many stat) "" input

Ваши тестовые данные показывают, что вы намерены проанализировать все это. Вы, вероятно, должны съесть пробел в конце, а затем использовать eof чтобы убедиться, что он потребляет весь ввод.

Наконец, на входе "x = arr[4]>-1" Я уверен, что лексер считает >- один токен, как и собственная грамматика Хаскелла. Таким образом, в этом случае парсер был корректным (и выдаст ошибку, если вы добавите eof Я предложил). Обратите внимание, что этого не происходит с операторами присваивания, потому что они не анализируются синтаксическим анализатором выражений Parsec.

Вот вывод, который я получаю после внесения этих изменений (прошу прощения за мое странное приглашение GHCi):

∀x. x ⊢ parseGML "x=-3"
Right [Assign (Just "x") (UnOp "-" (IntLit 3))]
∀x. x ⊢ parseGML "x = arr[4]>-1"
Left (line 1, column 11):
unexpected '>'
expecting ";", "if", identifier, "{" or end of input
∀x. x ⊢ parseGML "x = arr[4]> -1"
Right [Assign (Just "x") (Op ">" (Var "arr") (UnOp "-" (IntLit 1)))]
∀x. x ⊢ parseGML "x = 5+-3"
Left (line 1, column 6):
unexpected '+'
expecting ";", "if", identifier, "{" or end of input
∀x. x ⊢ parseGML "x = 5+ -3"
Right [Assign (Just "x") (Op "+" (IntLit 5) (UnOp "-" (IntLit 3)))]
∀x. x ⊢

Ваша строка 128:

assignStat = do ident <- (optionMaybe $ try $ variable >> symbol "=")

Я хочу сосредоточиться на этом немного:

variable >> symbol "="

Что это делает:

  • разбирает переменную, отбрасывая результат разбора
  • разбирает = знак
  • возвращает результат анализа = в результате общего разбора

То, что вы хотите, чтобы вместо этого произошло:

  • разбирать переменную
  • разобрать = токен, выбрасывая результат разбора
  • вернуть результат анализа переменной как результат общего разбора

Что изменить этот фрагмент кода на:

variable <* symbol "="

(и вам нужно будет импортировать (<*) от Control.Applicative)

За исключением того, что это не так просто: выражения имеют следующие типы:

variable >> symbol "=" :: Parser String
variable <* symbol "=" :: Parser Expr

Вы должны решить для себя, variable действительно правильный синтаксический анализатор для вызова в этой точке, или является ли первое поле Align вместо этого конструктор должен быть Maybe Exprили нужно ли это исправить другим способом.

Другие вопросы по тегам