Haskell Parsec - 使用自定义令牌时错误消息的帮助不大

2024-02-08

我正在研究分离解析器的词法分析和解析阶段。经过一些测试后,我意识到当我使用 Parsecs Char 令牌之外的一些令牌时,错误消息的帮助不大。

以下是使用 Char 令牌时 Parsec 错误消息的一些示例:

ghci> P.parseTest (string "asdf" >> spaces >> string "ok") "asdf  wrong"
parse error at (line 1, column 7):
unexpected "w"
expecting space or "ok"


ghci> P.parseTest (choice [string "ok", string "nop"]) "wrong"
parse error at (line 1, column 1):
unexpected "w"
expecting "ok" or "nop"

因此,当发现意外字符串时,字符串解析器会显示预期的字符串,而选择解析器会显示替代字符串。

但是当我对我的令牌使用相同的组合器时:

ghci> Parser.parseTest ((tok $ Ide "asdf") >> (tok $ Ide "ok")) "asdf  "
parse error at "test" (line 1, column 1):
unexpected end of input

在这种情况下,它不会打印预期的内容。

ghci> Parser.parseTest (choice [tok $ Ide "ok", tok $ Ide "nop"]) "asdf  "
parse error at (line 1, column 1):
unexpected (Ide "asdf","test" (line 1, column 1))

当我使用choice,它不打印替代方案。

我希望这种行为与组合器函数有关,而不是与令牌有关,但似乎我错了。我怎样才能解决这个问题?

这是完整的词法分析器+解析器代码:

Lexer:

module Lexer
    ( Token(..)
    , TokenPos(..)
    , tokenize
    ) where

import Text.ParserCombinators.Parsec hiding (token, tokens)
import Control.Applicative ((<*), (*>), (<$>), (<*>))

data Token = Ide String
           | Number String
           | Bool String
           | LBrack
           | RBrack
           | LBrace
           | RBrace
           | Keyword String
    deriving (Show, Eq)

type TokenPos = (Token, SourcePos)

ide :: Parser TokenPos
ide = do
    pos <- getPosition
    fc  <- oneOf firstChar
    r   <- optionMaybe (many $ oneOf rest)
    spaces
    return $ flip (,) pos $ case r of
                 Nothing -> Ide [fc]
                 Just s  -> Ide $ [fc] ++ s
  where firstChar = ['A'..'Z'] ++ ['a'..'z'] ++ "_"
        rest      = firstChar ++ ['0'..'9']

parsePos p = (,) <$> p <*> getPosition

lbrack = parsePos $ char '[' >> return LBrack
rbrack = parsePos $ char ']' >> return RBrack
lbrace = parsePos $ char '{' >> return LBrace
rbrace = parsePos $ char '}' >> return RBrace


token = choice
    [ ide
    , lbrack
    , rbrack
    , lbrace
    , rbrace
    ]

tokens = spaces *> many (token <* spaces)

tokenize :: SourceName -> String -> Either ParseError [TokenPos]
tokenize = runParser tokens ()

Parser:

module Parser where

import Text.Parsec as P
import Control.Monad.Identity
import Lexer

parseTest  :: Show a => Parsec [TokenPos] () a -> String -> IO ()
parseTest p s =
    case tokenize "test" s of
        Left e -> putStrLn $ show e
        Right ts' -> P.parseTest p ts'

tok :: Token -> ParsecT [TokenPos] () Identity Token
tok t = token show snd test
  where test (t', _) = case t == t' of
                           False -> Nothing
                           True  -> Just t

解决方案:

好的,在 fp4me 的回答并更仔细地阅读 Parsec 的 Char 源代码之后,我得到了以下结果:

{-# LANGUAGE FlexibleContexts #-}
module Parser where

import Text.Parsec as P
import Control.Monad.Identity
import Lexer

parseTest  :: Show a => Parsec [TokenPos] () a -> String -> IO ()
parseTest p s =
    case tokenize "test" s of
        Left e    -> putStrLn $ show e
        Right ts' -> P.parseTest p ts'


type Parser a = Parsec [TokenPos] () a

advance :: SourcePos -> t -> [TokenPos] -> SourcePos
advance _ _ ((_, pos) : _) = pos
advance pos _ [] = pos

satisfy :: (TokenPos -> Bool) -> Parser Token
satisfy f = tokenPrim show
                      advance
                      (\c -> if f c then Just (fst c) else Nothing)

tok :: Token -> ParsecT [TokenPos] () Identity Token
tok t = (Parser.satisfy $ (== t) . fst) <?> show t

现在我收到相同的错误消息:

ghci> Parser.parseTest(选择[tok $ Ide“ok”,tok $ Ide“nop”])“asdf”
解析错误位于(第 1 行,第 1 列):
意外(Ide“asdf”,“test”(第 1 行,第 3 列))
期待 Ide“ok”或 Ide“nop”


解决方案的开始可以是在解析器中定义您的选择函数, 使用特定的意外函数来覆盖意外错误,最后 使用<?>运算符覆盖预期消息:

mychoice [] = mzero
mychoice (x:[]) = (tok x <|> myUnexpected) <?> show x 
mychoice (x:xs) = ((tok x <|> mychoice xs) <|> myUnexpected)  <?> show (x:xs)

myUnexpected =  do 
             input <- getInput 
             unexpected $ (id $ first input )
           where 
            first [] = "eof"
            first (x:xs) = show $ fst x

并像这样调用你的解析器:

ghci> Parser.parseTest (mychoice [Ide "ok", Ide "nop"]) "asdf  "
parse error at (line 1, column 1):
unexpected Ide "asdf"
expecting [Ide "ok",Ide "nop"]
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Haskell Parsec - 使用自定义令牌时错误消息的帮助不大 的相关文章

随机推荐

  • 如何同时使用 telegram bot python

    我不知道如何使用 python 在电报中使用机器人进行多进程 我创建了一个线程 但如果该线程未完成 机器人将无法回复消息 horaPurga now replace hour 23 minute 36 second 59 microseco
  • RESTEasy - javax.ws.rs.NotFoundException:找不到完整路径的资源

    我尝试在 GWT 项目中使用 RESTEasy 实现 REST 服务 但是当我进入相应的 URI 时 应用程序返回 Grave failed to execute javax ws rs NotFoundException Could no
  • 不活动后会话自动注销

    快速会话中是否有内置功能 可以在给定的不活动时间后启用自动注销 我如下使用它 并希望它在会话半小时不活动时注销 app use session key sessid secret This is secret resave true sav
  • C#“使用”块

    我有类似下面的代码 这里有人提到 WebClient Stream 和 StreamReader 对象都可以从使用块中受益 两个简单的问题 1 这个小片段在使用块时会是什么样子 我自己做研究没有问题 所以资源链接很好 但只看一个例子会更快更
  • 与具有私有成员函数的类相比,未命名命名空间中的自由函数有什么好处?

    与拥有不带任何参数的私有类成员函数并直接访问成员变量相比 拥有自由函数 在匿名命名空间中并且只能在单个源文件中访问 并将所有变量作为参数发送有什么优势 header class A int myVariable void DoSomethi
  • C# 中的赋值运算符

    据我了解 与 C 不同 在 C 中不可能重写赋值运算符 如果我们想要将类 C 的实例 i1 分配给另一个实例 i2 C 类 则有必要创建一个复制方法 但困境来了 我有一个通用的 T 类 public class Node
  • 如何选择 div 内的图像来更改其来源?

    我有以下 div 并且我知道该 DIV 的选择器 Id div class event img src Content Images Icons calendar16 png Event Name div 但我不知道 图像是什么 我需要一些
  • 是否有可能在 Swift 中创建一个仅限于一个类的数组扩展?

    我可以制作一个仅适用于字符串等的数组扩展吗 从 Swift 2 开始 现在可以通过以下方式实现协议扩展 为符合类型提供方法和属性实现 可选地受到附加约束的限制 一个简单的例子 为所有符合的类型定义一个方法 到SequenceType 例如A
  • Hive 中的 ParseException

    我正在尝试使用UDF在蜂巢中 但是当我尝试使用创建临时函数时userdate as unixtimeToDate 我得到这个异常 hive gt create temporary function userdate1 as unixtime
  • Mod_rewrite:在特定页面上强制使用 SSL。在非安全页面上添加 www

    我知道这是一个常见的话题 但我已尽最大努力借助在网上搜索到的解决方案来解决它 我们有一个链接到子域 secure mysite com 的证书 我们希望实现以下目标 我们需要在以下路径及其子页面上强制使用 SSL http mysite c
  • .NET 字符串操作区分大小写吗?

    NET 字符串函数是这样的吗IndexOf blah 区分大小写 据我所知 它们不是 但出于某种原因 我在我的应用程序中看到了错误 其中查询字符串中的文本采用驼峰式大小写 如 UserID 并且我正在测试IndexOf userid 是的
  • JavaScript 中的 Char 数组到 Int32

    我有一个 char 数组 data 和一个 Int32 dictIdFrame 我希望 dictIdFrame 采用 data i i 3 的 ASCII 0 255 值 我的意思是四个字节变成一个 int32 其中 data i 是不太重
  • android.util.Log 中的错误或功能? - Log.isLoggable(DEBUG) = false 但 Log.d() 未禁用

    更新 重新制定问题和标题 我一直认为昂贵的 android 日志记录方法可以通过询问日志记录是否像这样活跃来优化 import android util Log if Log isLoggable MyContext Log DEBUG L
  • 用于将条件数据复制到特定单元格的 VBA 宏

    我是 VBA 编程新手 我正在寻找从匹配条件的不同工作表中获取数据 然后从一个特定单元格复制并粘贴到另一个特定单元格 7 次 我的代码不起作用 我正在寻求改进它 当我运行代码时 我在 IF 语句开头被标记为运行时错误 1004 方法 对象范
  • Twitter 的 Bootstrap Datepicker 缺少 Glyphicons

    我正在尝试使用引导程序中的日期选择器 http eternicode github io bootstrap datepicker http eternicode github io bootstrap datepicker 并且一切正常
  • JNI 和构造函数

    我有一个已编译的库 需要在项目中使用 简而言之 它是一个用于与特定硬件交互的库 我拥有的是 a 和 dll 库文件 分别适用于 Linux 和 Windows 以及一堆 C h 头文件 其中包含其中描述的所有公共函数和类 问题是该项目需要使
  • 嵌入式Tomcat:如何配置请求线程数

    In 嵌入式Tomcat 我该如何配置请求线程数 我似乎无法让它发挥作用 我尝试了所有这些但没有成功 tomcat getConnector setProperty maxThreads 20 tomcat getConnector set
  • Python 中的裸词/新关键字

    我想看看是否可以定义新的关键字 或者 正如它们所称的那样销毁所有软件的 WAT 谈话 https www destroyallsoftware com talks wat当讨论 Ruby 时 用 Python 来讨论 我想出了一个在其他地方
  • C# 中的事件 - 定义和示例 [关闭]

    很难说出这里问的是什么 这个问题是含糊的 模糊的 不完整的 过于宽泛的或修辞性的 无法以目前的形式得到合理的回答 如需帮助澄清此问题以便重新打开 访问帮助中心 help reopen questions 事件发生在什么地方C 你能给我一个清
  • Haskell Parsec - 使用自定义令牌时错误消息的帮助不大

    我正在研究分离解析器的词法分析和解析阶段 经过一些测试后 我意识到当我使用 Parsecs Char 令牌之外的一些令牌时 错误消息的帮助不大 以下是使用 Char 令牌时 Parsec 错误消息的一些示例 ghci gt P parseT