Чтение списка операторов и окончание одним выражением, когда операторы могут быть выражениями - PullRequest
0 голосов
/ 28 ноября 2018

Я столкнулся с проблемой, когда я хочу проанализировать блок кода со следующим синтаксисом

{
    <stmt>;
    <stmt>;
    <stmt>;
    <expr>
}

Оператор может иметь форму <expr>;.Это отключает Parsec таким способом, который я не знаю, как исправить.Вероятно, я просто новичок в Haskell и библиотеке Parsec, но я не знаю, где искать решение проблемы.Я написал пример, который отражает мою точную проблему.

При вводе { 5; 5; 5 } происходит сбой на третьем 5, потому что он ожидает присутствия ;.Как мне обойти это?

import           Text.ParserCombinators.Parsec
import           Text.ParserCombinators.Parsec.Combinator

parseIdentifier = do
    first <- letter
    rest  <- many $ letter <|> digit <|> char '_'
    return $ first : rest

parseExpr = parseIdentifier <|> many1 digit


parseStmt = parseExpr <* char ';'

parseBlock = between
    (char '{' >> spaces)
    (spaces >> char '}')
    (do
        stmts <- try $ parseStmt `sepBy` spaces
        parseExpr
    )

readParser :: Parser String -> String -> String
readParser parser input = case parse parser "dusk" input of
    Left  err -> show err
    Right val -> val

main = interact $ readParser parseBlock

Ответы [ 2 ]

0 голосов
/ 28 ноября 2018

Вместо sepBy такие проблемы часто могут быть решены с помощью manyTill, хитрый момент заключается в том, чтобы сохранить входные данные, которые не потребляются manyTill, для этого нужно использовать try $ lookAhead

Примечание: причину можно найти в исходном коде Parsec.Внутренне manyTill использует <|>, поэтому try вступает в силу, а lookAhead может сохранять ввод при применении монадного связывания >>=, >>

Итак, коррекциявыглядеть так:

parseBlock = between
    (char '{' >> spaces)
    (spaces >> char '}')
    (do
        stmts <- manyTill (parseStmt <* spaces) 
                          (try $ lookAhead (parseExpr >> space))
        parseExpr
    )

Приведенный выше парсер просто возвращает вывод parseExpr, то есть 5, если это ваше намерение, его можно упростить с помощью:

manyTill (parseStmt <* spaces) (try $ lookAhead (parseExpr >> space)) >> parseExpr

если вам действительно нужна разобранная строка операторов, она становится:

(do
    stmts <- manyTill (parseStmt <* spaces) 
                      (try $ lookAhead (parseExpr >> space))
    expr  <- parseExpr
    return (concat (stmts ++ [expr]))
)

и возвращает 555

0 голосов
/ 28 ноября 2018

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

Чтобы исправить это, я предлагаю следующее улучшение

parseBlock = between
    (char '{' >> spaces)
    (spaces >> char '}')
    (do
        stmts <- try $ many $ spaces *> parseStmt
        spaces
        parseExpr
    )
...