Обратите внимание, что после публикации этого вопроса мне удалось найти решение самостоятельно. См. Конец этого вопроса для моего окончательного ответа.
В данный момент я работаю над небольшим парсером для org-mode документов, и в этих документах заголовки могут иметь заголовок и могут дополнительно содержать список тегов в заголовке :
* Heading :foo:bar:baz:
Однако мне сложно написать парсер для этого. Вот с чем я сейчас работаю:
import Control.Applicative
import Text.ParserCombinators.Parsec
data Node = Node String [String]
deriving (Show)
myTest = parse node "" "Some text here :tags:here:"
node = Node <$> (many1 anyChar) <*> tags
tags = (char ':') >> (sepEndBy1 (many1 alphaNum) (char ':'))
<?> "Tag list"
Хотя мой простой парсер tags
работает, он не работает в контексте node
, потому что все символы используются для разбора заголовка заголовка (many1 anyChar
). Кроме того, я не могу изменить этот синтаксический анализатор для использования noneOf ":"
, потому что :
допустим в заголовке. На самом деле, он особенный, только если он находится в списке тегов, в самом конце строки.
Есть идеи, как я могу разобрать эти необязательные данные?
Кроме того, это мой первый настоящий проект на Haskell, поэтому, если Parsec даже не является подходящим инструментом для работы - не стесняйтесь указывать на это и предлагать другие варианты!
Хорошо, я получил полное решение, но оно нуждается в рефакторинге. Следующие работы:
import Control.Applicative hiding (many, optional, (<|>))
import Control.Monad
import Data.Char (isSpace)
import Text.ParserCombinators.Parsec
data Node = Node { level :: Int, keyword :: Maybe String, heading :: String, tags :: Maybe [String] }
deriving (Show)
parseNode = Node <$> level <*> (optionMaybe keyword) <*> name <*> (optionMaybe tags)
where level = length <$> many1 (char '*') <* space
keyword = (try (many1 upper <* space))
name = noneOf "\n" `manyTill` (eof <|> (lookAhead (try (tags *> eof))))
tags = char ':' *> many1 alphaNum `sepEndBy1` char ':'
myTest = parse parseNode "org-mode" "** Some : text here :tags: JUST KIDDING :tags:here:"
myTest2 = parse parseNode "org-mode" "* TODO Just a node"