簡體   English   中英

Haskell / Parsec:如何將Text.Parsec.Token與Text.Parsec.Indent一起使用(來自縮進包)

[英]Haskell/Parsec: how do I use Text.Parsec.Token with Text.Parsec.Indent (from the indents package)

Haskell的Parsec的indents包提供了一種解析縮進式語言(如Haskell和Python)的方法。 它重新定義了Parser類型,那么如何使用Parsec的Text.Parsec.Token模塊導出的令牌解析器函數,這些函數是普通的Parser類型?

背景

Parsec附帶了大量模塊 大多導出一堆有用解析器(例如newlineText.Parsec.Char ,它解析新行)或解析器組合(例如count npText.Parsec.Combinator ,它運行解析器P,N次)

但是,模塊Text.Parsec.Token想要導出由用戶參數化的函數和正在解析的語言的特征,因此,例如, braces p函數將在解析'{'后運行解析器p在解析'}'之前,忽略注釋之類的東西,其語法取決於您的語言。

Text.Parsec.Token實現這一點的方式是它導出一個函數makeTokenParser ,你調用它,給它你特定語言的參數(就像注釋的樣子),它返回一個包含所有函數的記錄。 Text.Parsec.Token ,適合您指定的語言。

當然,在縮進式語言中,這些需要進一步調整(也許?這里是我不確定的地方 - 我稍后會解釋)所以我注意到(可能是過時的)IndentParser包提供了一個模塊Text.ParserCombinators.Parsec.IndentParser.Token ,它看起來是Text.Parsec.Token替代Text.Parsec.Token

我應該在某些時候提到所有的Parsec解析器都是monadic函數,所以它們用狀態做神奇的事情,這樣錯誤消息可以說出源文件中的哪一行和哪一行出現了錯誤

我的問題

由於一些小的原因,在我看來,縮進包或多或少是當前版本的IndentParser,但是它沒有提供看起來像Text.ParserCombinators.Parsec.IndentParser.Token的模塊,它只提供Text.Parsec.Indent ,所以我想知道如何從Text.Parsec.Token獲取所有令牌解析器 (如reserved "something"解析保留關鍵字“某事”,或者像我之前提到的那些braces )。

在我看來,(新的) Text.Parsec.Indent通過某種Text.Parsec.Indent狀態魔法來處理源代碼的哪些列位,因此它不需要修改像whiteSpace那樣的令牌解析器Text.Parsec.Token ,這可能是它沒有提供替換模塊的原因。 但是我遇到類型問題。

你看,沒有Text.Parsec.Indent ,我所有的解析器都是Parser Something ,其中Something是返回類型, Parser是Text.Parsec.String中定義的類型別名

type Parser = Parsec String ()

但是使用Text.Parsec.Indent ,我使用自己的定義,而不是導入Text.Parsec.String

type Parser a = IndentParser String () a

這使我的所有解析器類型為IndentParser String () Something ,其中IndentParser在Text.Parsec.Indent中定義。 但是我從makeTokenParser中的Text.Parsec.Token獲取的令牌解析器的類型錯誤。

如果現在這沒有多大意義,那是因為我有點失落。 這里討論了類型問題。


我得到的錯誤是我嘗試用另一個替換上面的Parser的一個定義,但是當我嘗試使用Text.Parsec.Token一個令牌解析器時,我得到了編譯錯誤

Couldn't match expected type `Control.Monad.Trans.State.Lazy.State
                                Text.Parsec.Pos.SourcePos'
            with actual type `Data.Functor.Identity.Identity'
Expected type: P.GenTokenParser
                 String
                 ()
                 (Control.Monad.Trans.State.Lazy.State Text.Parsec.Pos.SourcePos)
  Actual type: P.TokenParser ()

鏈接

遺憾的是,上面的示例都沒有像Text.Parsec.Token中那樣使用令牌解析器。

你想做什么?

聽起來你想讓你的解析器在任何地方被定義為類型

Parser Something

(其中Something是返回類型)並通過隱藏和重新定義通常從Text.Parsec.String或類似方法導入的Parser類型來實現此功能。 您仍然需要導入一些Text.Parsec.String ,以使Stream成為monad的實例; 用這條線做到這一點:

import Text.Parsec.String ()

您對Parser定義是正確的。 或者等效地(對於那些在評論中聊天的人)你可以使用

import Control.Monad.State
import Text.Parsec.Pos (SourcePos)

type Parser = ParsecT String () (State SourcePos)

並且可能在顯示此定義的文件中import Text.Parsec.Indent (IndentParser)

錯誤,牆上的錯誤

您的問題是您正在查看編譯器錯誤消息的錯誤部分。 你專注於

Couldn't match expected type `State SourcePos' with actual type `Identity'

當你應該專注於

Expected type: P.GenTokenParser ...
  Actual type: P.TokenParser ...

它匯編!

你從Text.Parsec.Token “導入”解析器的Text.Parsec.Token ,你實際做的當然(正如你簡要提到的)首先定義一個記錄你的語言參數,然后將它傳遞給函數makeTokenParser ,它返回一個包含的記錄令牌解析器。

因此,您必須有一些看起來像這樣的行:

import qualified Text.Parsec.Token as P

beetleDef :: P.LanguageDef st
beetleDef =
    haskellStyle {
        parameters, parameters etc.
        }

lexer :: P.TokenParser ()
lexer = P.makeTokenParser beetleDef

...但是P.LanguageDef st只是GenLanguageDef String st Identity ,而P.TokenParser ()實際上是GenTokenParser String () Identity

您必須將類型聲明更改為以下內容:

import Control.Monad.State
import Text.Parsec.Pos (SourcePos)
import qualified Text.Parsec.Token as P

beetleDef :: P.GenLanguageDef String st (State SourcePos)
beetleDef =
    haskellStyle {
        parameters, parameters etc.
        }

lexer :: P.GenTokenParser String () (State SourcePos)
lexer = P.makeTokenParser beetleDef

......就是這樣! 這將允許您的“導入”令牌解析器具有類型ParsecT String () (State SourcePos) Something ,而不是Parsec String () Something (它是ParsecT String () Identity Something的別名),您的代碼現在應該編譯。

(為了最大限度的通用性,我假設您可能在一個文件中定義Parser類型,該文件與您定義實際解析器函數的文件分開並導入。因此,這兩個重復的import語句。)

謝謝

非常感謝Daniel Fischer幫我解決這個問題。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM