Я новичок в Haskell, Parsec и написал парсеры в целом. Я пытаюсь разобрать простой язык, который (упрощая его далее для этого вопроса) состоит просто из строк вложенных скобок, например. [[][]][]
.Как разобрать эту грамматику в Parsec? (необычный случай левой рекурсии)
У меня есть код Haskell ниже, который отлично работает. Тем не менее, я хотел бы расширить его, чтобы несогласованные скобки соответствовали концу строки. Так, например, ]][][[
должен быть эквивалентен [[]][][[]]
, а []]
должен быть эквивалентен [[]]
. Выполнение этого для открытых скобок, соответствующих концу строки, легко, но для закрытых скобок, соответствующих началу строки, получается левая рекурсия и бесконечные петли, и я не нашел способ решить эту проблему. Я не уверен, связано ли это с тем, как я думаю о грамматике или о том, как я использую библиотеку Parsec, но в любом случае я был бы признателен за то, что мне показали путь вперед.
Вот рабочий код у меня есть:
{-# LANGUAGE NoMonomorphismRestriction #-}
import qualified Text.Parsec as Parsec
import Control.Applicative
-- for testing
parse rule text = Parsec.parse rule "(source)" text
data Expr = Brackets [Expr]
deriving(Show)
openBracket = Parsec.char '['
closeBracket = Parsec.char ']'
parseBrackets = do
expr <- Parsec.between openBracket closeBracket parseExpr
return $ Brackets expr
parseExpr = Parsec.many parseBrackets
Если я хочу закрытые скобки, чтобы соответствовать против конца строки, я могу просто изменить определение closeBracket
к
closeBracket = (Parsec.char ']' >> return()) <|> Parsec.eof
Но, несмотря на довольно немного проб и ошибок. Я не нашел решения, чтобы совпадение не имело значения ]
s против начала строки. Я знаю, что обычное решение левой рекурсии в Parsec - это функция chainl1
, но это, похоже, довольно специализировано для инфиксных операторов, и я не вижу способа использовать его здесь.
Это здорово, я многому научился от него.(Я особенно ценю любую магию, которую вы использовали для чтения читаемых типов анализаторов, а не таких вещей, как «Parsec.Stream sm Char => Parsec.ParsecT sum [Expr]», как я раньше.) Но ваш синтаксический анализатор, похоже, игнорирует непревзойденные ']' если он сначала найдет набор совпадающих скобок, так что '[]]' выходит эквивалентно '[]' вместо '[[]]'. (Я добавил этот тестовый пример к вопросу, и я очень сожалею о том, что не сделал этого намерения более ясным. Наверное, я могу исправить это сам, когда выясню, что делает функция 'parseStart'.) – Nathaniel
Я отправил автоответ, который решает эту проблему, хотя он немного похож на обман ... – Nathaniel