Haskell 带分隔符的Parsec置换解析器

Haskell 带分隔符的Parsec置换解析器,haskell,parsec,Haskell,Parsec,我想解析汇编程序。我有一个用于解析汇编地址的固定格式:[register+offset+label]我实现了寄存器、偏移量和标签的解析器。现在我想创建一个解析器来解析整个地址 我想要接受的组合: [register] [offset] [label] [register + offset] [register + label] [offset + label] [register + offset + label] [] [register offset] [register + ] ...

我想解析汇编程序。我有一个用于解析汇编地址的固定格式:
[register+offset+label]
我实现了寄存器、偏移量和标签的解析器。现在我想创建一个解析器来解析整个地址

我想要接受的组合:

[register]
[offset]
[label]
[register + offset]
[register + label]
[offset + label]
[register + offset + label]
[]
[register offset]
[register + ]
...
我不想接受的是:

[register]
[offset]
[label]
[register + offset]
[register + label]
[offset + label]
[register + offset + label]
[]
[register offset]
[register + ]
...
当然,简单的解决方案是:

choice $ try (parseRegister >>= \r -> Address (Just r) Nothing Nothing)
       <|> try ...
choice$try(parseRegister>=\r->Address(只是r)Nothing Nothing)
尝试
但它是丑陋的,不能与更多类型的元素很好地匹配。所以我在寻找一种更清洁的解决方案。

类似的:

parsePlus = many1 (char ' ') >> char '+' >> many1 (char ' ')

parseRegisterModified = parsePlus >> parseOffsetLabel

parseOffsetModified = parsePlus >> parseLabel

parseRegister' = do
    Address r _ _ <- parseRegister 
    optionMaybe parseRegisterModified >>=
    return $ maybe 
           (Address r Nothing Nothing) 
           (\Address _ o l -> Address r o l) 

parseOffset' = do
    Address _ o _ <- parseOffset 
    optionMaybe parseOffsetModified >>=
    return $ maybe 
           (Address Nothing o Nothing) 
           (\Address _ _ l -> Address Nothing o l)

parseOffsetLabel = try parseOffset' <|> parseLabel

parseAddress = 
     try parseRegister'
     <|> parseOffset'
     <|> parseLabel
parsePlus=many1(字符“”)>>char'+'>>many1(字符“”)
parseRegisterModified=parsePlus>>parseOffsetLabel
parseOffsetModified=parsePlus>>parseLabel
解析寄存器'=do
地址r\uu>=
返回$maybe
(地址r Nothing Nothing)
(\Address\uo l->Address r o l)
parseOffset'=do
地址uo>=
返回$maybe
(无地址或无地址)
(\Address\uuul->Address Nothing o o l)
parseOffsetLabel=尝试parseOffset的parseLabel
解析地址=
尝试解析寄存器'
parseOffset'
解析标签

使用
Monoids
sepBy1
可以得到更优雅的解决方案

但它允许编写
[寄存器+寄存器]
(在我们的例子中,将两者相加)


如果对表格重新排序,您会看到一系列选项:

[register + offset + label]
[register + offset        ]
[register          + label]
[register                 ]
[           offset + label]
[           offset        ]
[                    label]
可以编写的语法:

address      = '[' (register ('+' offset-label)? | offset-label) ']'
offset-label = offset ('+' label)? | label
在应用程序风格中,它非常简单,通过将所有内容包装在构造函数中只会产生轻微的噪音:

parseAddress :: Parser Address
parseAddress = do
  (register, (offset, label)) <- between (char '[') (char ']') parseRegisterOffsetLabel
  return $ Address register offset label

parseRegisterOffsetLabel :: Parser (Maybe Register, (Maybe Offset, Maybe Label))
parseRegisterOffsetLabel = choice
  [ (,)
    <$> (Just <$> parseRegister)
    <*> option (Nothing, Nothing) (char '+' *> parseOffsetLabel)
  , (,) Nothing <$> parseOffsetLabel
  ]

parseOffsetLabel :: Parser (Maybe Offset, Maybe Label)
parseOffsetLabel = choice
  [ (,)
    <$> (Just <$> parseOffset)
    <*> option Nothing (char '+' *> (Just <$> parseLabel))
  , (,) Nothing . Just <$> parseLabel
  ]
我们可以稍微清理一下这些实现:

parseRegisterOffsetLabel = choice
  [ (,)
    <$> just parseRegister
    <*> option (Nothing, Nothing) (plus parseOffsetLabel)
  , (,) Nothing <$> parseOffsetLabel
  ]

parseOffsetLabel = choice
  [ (,)
    <$> just parseOffset
    <*> option Nothing (plus (just parseLabel))
  , (,) Nothing <$> just parseLabel
  ]
parseRegisterOffsetLabel=选项
[ (,)
只需解析寄存器
选项(无,无)(加上parseOffsetLabel)
,(,)无解析OffsetLabel
]
parseOffsetLabel=choice
[ (,)
只是解析偏移量
选项Nothing(加上(仅解析标签))
,(,)没什么,只是解析标签
]
然后考虑重复,为我们提供一个体面的最终解决方案:

parseChain begin def rest = choice
  [ (,) <$> just begin <*> option def (plus rest)
  , (,) Nothing <$> rest
  ]

parseRegisterOffsetLabel = parseChain
  parseRegister (Nothing, Nothing) parseOffsetLabel

parseOffsetLabel = parseChain
  parseOffset Nothing (just parseLabel)
parseChain begin def rest=choice
[(,)刚刚开始选项def(加上rest)
没有什么可以休息
]
parseRegisterOffsetLabel=parseChain
parseRegister(无,无)parseOffsetLabel
parseOffsetLabel=parseChain
parseOffset Nothing(仅parseLabel)

我会让你处理
+
周围和
[]

内部的空白,我一直在寻找类似的内容,并找到了 . 虽然我的案例可以独立于低级平台进行扩展

在你的情况下可能看起来像

operand = do
    (r, o, l) <- runPermsSep (char '+') $ (,,)
        <$> maybeAtom register
        <*> maybeAtom offset
        <*> maybeAtom label
    -- backtrack on inappropriate combination
    when (null $ catMaybes [r, o, l]) . fail $ "operand expected"
    return (r, o, l)
操作数=do

(r、o、l)一个轻量级技巧是使用
Applicative
样式和
optionMaybe
。像这样
试试$Address optionMaybe parseRegister optionMaybe parseOffset optionMaybe parseLabel
。这也适用于一元格式。但我的主要问题是我必须解析那些签名!这些是文本字符串。您可能想使用
sebby
获取字符串列表,然后从中计算
地址
值?谢谢。它似乎是正确的解析器,但它是一个重量级的解决方案。这很有趣,但允许
[register+register]
是不可接受的。不错。我做了一些修改以适应我的问题。我所做的编辑:我在前面创建了一个
tuple::Parser a->Parser b->Parser(a,b)
combinator并使用了它,因此不需要使用应用程序
。使用
符号“+”
处理空白。偏移量(0偏移量)和寄存器(始终为0的$0寄存器)都有默认值,所以我使用它们而不是什么都不使用。@BoldizárNémeth:很高兴能提供帮助。我只能提供一个通用的解决方案,因为我不知道您的数据类型。
operand = do
    (r, o, l) <- runPermsSep (char '+') $ (,,)
        <$> maybeAtom register
        <*> maybeAtom offset
        <*> maybeAtom label
    -- backtrack on inappropriate combination
    when (null $ catMaybes [r, o, l]) . fail $ "operand expected"
    return (r, o, l)