我想解析汇编程序。我有一个固定的格式来解析汇编地址:[ 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 ...
但它很难看,而且随着更多类型的元素不能很好地扩展。所以我正在寻找一个更清洁的解决方案。
答案 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)
使用Monoids
和sepBy1
可以获得更优雅的解决方案。
但是它允许写[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(r
,o
,l
),我们会改变行为:
Sum
互相添加,First
选择第一个,Last
选择最后一个
... where
a `mappendA` a' = getFirst $ First a `mappend` First a'