Как лучше всего использовать WriterT с Parsec?

Вопрос нуба по Haskell.

Я весело провел время, написав несколько парсеров как с Parsec, так и с AttoParsec. Теперь я хочу собрать информацию в процессе синтаксического анализа (по сути, построить таблицу символов), и использование преобразователя монад WriterT кажется хорошим вариантом.

У меня это работает сейчас в этом простом примере:

module NewParse where

import qualified Text.ParserCombinators.Parsec as A
import Control.Monad.Trans (lift)
import Control.Monad.Writer (WriterT, tell,  runWriterT)
type WParser a = WriterT [String] A.Parser a 

data StoryToken = StoryToken Char deriving (Show)

getToken :: WParser StoryToken
getToken = do
  tell ["hello from Writer T"]
  c <- lift A.anyChar 
  return $ StoryToken c 

test = A.parse (runWriterT getToken) "story" "#"

Это прекрасно работает. Вызов теста в ghci дает мне это:

*NewParse> test
Right (StoryToken '#',["hello"])

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

Есть ли способ легко раскрыть определенные функции из Parsec, чтобы мне не приходилось использовать подъем в коде?

Решение, которое сработало для меня, заключалось в создании моей собственной функции

anyChar = lift A.anyChar

Это круто, но потребовало бы создания подобных теневых функций для каждой функции, которую я использую из parsec.

И я не возражаю против этого, но просто подумал, есть ли лучший, менее шаблонный способ добиться того же самого.

Любая помощь с благодарностью принята :)


person Gary Liddon    schedule 22.10.2013    source источник
comment
Вообще говоря, вы не стали бы создавать таблицу символов при синтаксическом анализе с помощью Haskell. С кодированием AST на Haskell легко, потому что алгебраические типы соответствуют деревьям, а отображение символов на что-либо, кроме мономорфных типов, затруднено. В постобработке обычно используется среда, в которой хранятся привязки (аналогично таблице символов), но среды обычно могут сопоставлять идентификаторы с монотипами, например. при реализации функционального языка среда будет отображать идентификаторы в выражения. Короче говоря, я бы порекомендовал вам попытаться сделать больше с вашим AST и избегать отдельной таблицы символов.   -  person stephen tetley    schedule 22.10.2013


Ответы (1)


Parsec позволяет пользователям передавать состояние через парсеры, просто используя

addSym :: String -> Parsec String [String] ()
addSym = void . updateState . (:) -- append a string to the symbol list

type MyParser = Parsec String [String]

Это будет корректно обрабатывать откат.

person Daniel Gratzer    schedule 22.10.2013
comment
Предупреждение! ParsecT String () (Writer W) и WriterT W Parser не совпадают. В первом случае, если вы используете откат (try) и пишете что-то внутри ветки, которая заканчивается возвратом, эта вещь все равно появится в окончательном выводе. - person luqui; 22.10.2013
comment
@luqui Исправлено со следующим предложением - person Daniel Gratzer; 22.10.2013
comment
Вы действительно не исправили это. Переворачивать его - неправильное и вредное предложение (по причине, указанной @luqui); Я думаю, вы должны удалить его. См. также ro-che.info/articles/ - person Roman Cheplyaka; 23.10.2013