具有分隔符的Parsec置换解析器

时间:2013-08-06 13:22:21

标签: haskell parsec

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

我想接受的组合:

[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 ...

但它很难看,而且随着更多类型的元素不能很好地扩展。所以我正在寻找一个更清洁的解决方案。

4 个答案:

答案 0 :(得分:2)

如果您重新排序表格,您会看到它是一系列选择:

[register + offset + label]
[register + offset        ]
[register          + label]
[register                 ]
[           offset + label]
[           offset        ]
[                    label]

可能写的语法:

address      = '[' (register ('+' offset-label)? | offset-label) ']'
offset-label = offset ('+' label)? | label

在Applicative样式中,它非常简单,只是通过将所有内容包装在构造函数中而略微嘈杂:

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
  ]

如果我们添加一些实用功能:

plus :: Parser a -> Parser a
plus x = char '+' *> x

just :: Parser a -> Parser (Maybe a)
just = fmap Just

我们可以稍微清理这些实现:

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

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

然后考虑重复,给我们一个不错的最终解决方案:

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)

我会让你照顾+[]内的空白。

答案 1 :(得分:1)

类似的东西:

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

答案 2 :(得分:1)

我一直在寻找类似的东西并找到了 Control.Applicative.Permutation from action-permutations。虽然我的案例可以独立于低级平台进行扩展。

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

public partial class SomeForm : Window
{
    public SomeForm()
    {
        InitializeComponent();

        Meta.StaticPropertyChanged += MethodThatTriggersOnUpdate;
        ...
    }

    private void MethodThatTriggersOnUpdate(object sender, EventArgs e)
    {
        myImage.Dispatcher.BeginInvoke(
            (Action)(() => myImage.Source = new BitmapImage(
            new Uri("/MyProject;component/Images/myNewImage.gif", UriKind.Relative))));
    }
    ...
}

请注意,您实际上需要可选的排列解析器,该解析器至少需要一个可选元素,这使得您想要的解析器组合器非常具体。

答案 3 :(得分:0)

使用MonoidssepBy1可以获得更优雅的解决方案。

但是它允许写[register + register](在我们的例子中添加它们)

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

parseAddress1 = 
     try parseRegister
     <|> parseOffset
     <|> parseLabel

parseAddress = sepBy1 parsePlus parseAddress1 >>= return . mconcat

instance Monoid Address where
   mempty  = Address Nothing Nothing Nothing
   Address r o l `mappend` Address r' o' l' = 
           Address (r `mappendA` r') (o `mappendA` o') (l `mappendA` l')
   where
         a `mappendA` a' = fmap getSum $ fmap Sum a `mappend` fmap Sum a'

Sum a First a Last a选择Monoid(rol),我们会改变行为:

Sum互相添加,First选择第一个,Last选择最后一个

   ... where
      a `mappendA` a' = getFirst $ First a `mappend` First a'