Проблема с префиксным оператором 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
или нужно ли это исправить другим способом.