2013-09-14 3 views
6

Я использую библиотеку комбинаторов парсеров Trifecta, а мои парсеры выдает экземпляры типа данных AST. Я хочу, чтобы каждый экземпляр имел уникальный идентификатор (который является простым Int).Пользовательское состояние в Trifecta

В Parsec я создавал бы пользовательское состояние и увеличивал бы ID при необходимости. Как мы можем это сделать в Трифекте?

ответ

5

Вы можете улучшить монаду Parser с помощью трансформатора монады StateT, чтобы получить то, что вы хотите. Это хорошо сочетается с остальной библиотекой, так как большинство комбинаторов используют классы типов, а не конкретные типы (это означает, что вам не нужно много делать для работы кода). Вот достойный пример этого. Он анализирует грамматику с идентификаторами и символами, разделенными пробелами. Каждому идентификатору присваивается уникальный номер.

module Main where 
import Text.Trifecta 
import Control.Monad.State 
import Control.Applicative 
import Data.Monoid 

data Identifier = Identifier String Int deriving (Show) 

identifier :: StateT Int Parser Identifier 
identifier = do 
    name <- some letter 
    newId <- get 
    modify (+1) 
    return $ Identifier name newId 

symbolToken :: Parser Char 
symbolToken = oneOf "+-*/" 

data Token = IdentifierToken Identifier | SymbolToken Char deriving (Show) 

singleToken :: StateT Int Parser Token 
singleToken = try (IdentifierToken <$> identifier) <|> (SymbolToken <$> lift symbolToken) 

parseTokens :: StateT Int Parser [Token] 
parseTokens = singleToken `sepBy1` spaces 

testParse :: String -> Result [Token] 
testParse = parseString (evalStateT parseTokens 0) mempty 

test1 :: Result [Token] 
test1 = testParse "these are identifiers and + some/symbols -" 

test1 приводит:

Success [IdentifierToken (Identifier "these" 0) 
,IdentifierToken (Identifier "are" 1) 
,IdentifierToken (Identifier "identifiers" 2) 
,IdentifierToken (Identifier "and" 3) 
,SymbolToken '+',IdentifierToken (Identifier "some" 4) 
,SymbolToken '/',IdentifierToken (Identifier "symbols" 5),SymbolToken '-'] 
+0

Я только что обнаружил ответ неверен. Посмотрите, вы оцениваете состояние перед синтаксическим разбором, что неверно - в таких случаях использования, как если бы вы использовали это состояние для генерации уникальных идентификаторов. –

+1

Вы уверены, что это неправильно? Государство оценивается не только с самого начала, но и через всю программу. 'EvalStateT parseTokens 0' фактически просто устанавливает начальное значение состояния. Так я всегда это делал, и у него, похоже, нет никаких проблем. Если у вас есть пример того, как это дает неожиданный результат, я бы хотел его увидеть! –

+0

Правильно, я был неправ. Этот пример верен - наш пример имел опечатку в очень забавном месте, и мы искали ошибку в совершенно неправильном месте. Я удалю комментарии tommorow, потому что они устарели сейчас. Спасибо! :) –

Смежные вопросы