如何使用最新版本的Parsec.Indent库?
How to use the latest version of the Parsec.Indent library?
这个问题似乎是 this question, however either Parsec or the Indent library has changed since 2012 and none of the old examples I have found for the indent library compile with the latest versions 的重复。
我想为一种编程语言制作一个解析器,其中缩进是语法的一部分(用于指示范围),为了实现这一点,我想使用 Text.Parsec.Indent 库,但我我不知道如何使用它。我很清楚必须制作一些 modifications/custom 解析器类型,但我对 State monad 的有限知识和对 parsec 的表面理解似乎还不够。
假设您想为一个简单的整数列表创建一个解析器,如下所示。如何实现这一目标?
mylist
fstitem
snditem
我尝试基于互联网上流传的一些旧示例创建一个简单的解析器,看起来像这样,但它显然会产生一些类型错误:
import Control.Monad.State
import Text.Parsec hiding (State)
import Text.Parsec.Indent
import Text.Parsec.Pos
type IParser a = ParsecT String () (State SourcePos) a
parseInt :: IParser Integer
parseInt = read <$> many1 digit
parseIndentedInt :: IParser Integer
parseIndentedInt = indented *> parseInt
特别是这些:
Frontend/Parser.hs:14:20: error:
• Couldn't match type ‘Control.Monad.Trans.Reader.ReaderT
Text.Parsec.Indent.Internal.Indentation m0’
with ‘StateT SourcePos Data.Functor.Identity.Identity’
Expected type: IParser Integer
Actual type: ParsecT String () (IndentT m0) Integer
• In the expression: indented *> parseInt
In an equation for ‘parseIndentedInt’:
parseIndentedInt = indented *> parseInt
|
14 | parseIndentedInt = indented *> parseInt
| ^^^^^^^^^^^^^^^^^^^^
Frontend/Parser.hs:14:32: error:
• Couldn't match type ‘StateT
SourcePos Data.Functor.Identity.Identity’
with ‘Control.Monad.Trans.Reader.ReaderT
Text.Parsec.Indent.Internal.Indentation m0’
Expected type: ParsecT String () (IndentT m0) Integer
Actual type: IParser Integer
• In the second argument of ‘(*>)’, namely ‘parseInt’
In the expression: indented *> parseInt
In an equation for ‘parseIndentedInt’:
parseIndentedInt = indented *> parseInt
|
14 | parseIndentedInt = indented *> parseInt
| ^^^^^^^^
Failed, no modules loaded.
好的,在深入研究源代码并查看缩进 GitHub 存储库中的测试后,我设法创建了一个工作示例。
下面的代码可以解析一个简单的缩进列表:
import Text.Parsec as Parsec
import Text.Parsec.Indent as Indent
data ExampleList = ExampleList String [ExampleList]
deriving (Eq, Show)
plistItem :: Indent.IndentParser String () String
plistItem = Parsec.many1 Parsec.lower <* Parsec.spaces
pList :: Indent.IndentParser String () ExampleList
pList = Indent.withPos (ExampleList <$> plistItem <*> Parsec.many (Indent.indented *> pList))
useParser :: Indent.IndentParser String () a -> String -> a
useParser p src = helper res
where res = Indent.runIndent $ Parsec.runParserT (p <* Parsec.eof) () "<test>" src
helper (Left err) = error "Parse error"
helper (Right ok) = ok
用法示例:
*Main> useParser pList "mylist\n\tfstitem\n\tsnditem"
ExampleList "mylist" [ExampleList "fstitem" [],ExampleList "snditem" []]
请注意,useParser
函数执行了一些操作,实际上从 Either monad 中获取结果,并将文件结尾解析器放在提供的解析器后面。根据您的应用程序,您可能想要更改此设置。
另外,类型签名可以用这样的东西来缩短:
type IParser a = Indent.IndentParser String () a
plistItem :: IParser String
pList :: IParser ExampleList
useParser :: IParser a -> String -> a
这个问题似乎是 this question, however either Parsec or the Indent library has changed since 2012 and none of the old examples I have found for the indent library compile with the latest versions 的重复。
我想为一种编程语言制作一个解析器,其中缩进是语法的一部分(用于指示范围),为了实现这一点,我想使用 Text.Parsec.Indent 库,但我我不知道如何使用它。我很清楚必须制作一些 modifications/custom 解析器类型,但我对 State monad 的有限知识和对 parsec 的表面理解似乎还不够。
假设您想为一个简单的整数列表创建一个解析器,如下所示。如何实现这一目标?
mylist
fstitem
snditem
我尝试基于互联网上流传的一些旧示例创建一个简单的解析器,看起来像这样,但它显然会产生一些类型错误:
import Control.Monad.State
import Text.Parsec hiding (State)
import Text.Parsec.Indent
import Text.Parsec.Pos
type IParser a = ParsecT String () (State SourcePos) a
parseInt :: IParser Integer
parseInt = read <$> many1 digit
parseIndentedInt :: IParser Integer
parseIndentedInt = indented *> parseInt
特别是这些:
Frontend/Parser.hs:14:20: error:
• Couldn't match type ‘Control.Monad.Trans.Reader.ReaderT
Text.Parsec.Indent.Internal.Indentation m0’
with ‘StateT SourcePos Data.Functor.Identity.Identity’
Expected type: IParser Integer
Actual type: ParsecT String () (IndentT m0) Integer
• In the expression: indented *> parseInt
In an equation for ‘parseIndentedInt’:
parseIndentedInt = indented *> parseInt
|
14 | parseIndentedInt = indented *> parseInt
| ^^^^^^^^^^^^^^^^^^^^
Frontend/Parser.hs:14:32: error:
• Couldn't match type ‘StateT
SourcePos Data.Functor.Identity.Identity’
with ‘Control.Monad.Trans.Reader.ReaderT
Text.Parsec.Indent.Internal.Indentation m0’
Expected type: ParsecT String () (IndentT m0) Integer
Actual type: IParser Integer
• In the second argument of ‘(*>)’, namely ‘parseInt’
In the expression: indented *> parseInt
In an equation for ‘parseIndentedInt’:
parseIndentedInt = indented *> parseInt
|
14 | parseIndentedInt = indented *> parseInt
| ^^^^^^^^
Failed, no modules loaded.
好的,在深入研究源代码并查看缩进 GitHub 存储库中的测试后,我设法创建了一个工作示例。
下面的代码可以解析一个简单的缩进列表:
import Text.Parsec as Parsec
import Text.Parsec.Indent as Indent
data ExampleList = ExampleList String [ExampleList]
deriving (Eq, Show)
plistItem :: Indent.IndentParser String () String
plistItem = Parsec.many1 Parsec.lower <* Parsec.spaces
pList :: Indent.IndentParser String () ExampleList
pList = Indent.withPos (ExampleList <$> plistItem <*> Parsec.many (Indent.indented *> pList))
useParser :: Indent.IndentParser String () a -> String -> a
useParser p src = helper res
where res = Indent.runIndent $ Parsec.runParserT (p <* Parsec.eof) () "<test>" src
helper (Left err) = error "Parse error"
helper (Right ok) = ok
用法示例:
*Main> useParser pList "mylist\n\tfstitem\n\tsnditem"
ExampleList "mylist" [ExampleList "fstitem" [],ExampleList "snditem" []]
请注意,useParser
函数执行了一些操作,实际上从 Either monad 中获取结果,并将文件结尾解析器放在提供的解析器后面。根据您的应用程序,您可能想要更改此设置。
另外,类型签名可以用这样的东西来缩短:
type IParser a = Indent.IndentParser String () a
plistItem :: IParser String
pList :: IParser ExampleList
useParser :: IParser a -> String -> a