Parsec.Expr.buildExpressionParser的文档说:
相同优先级的前缀和后缀运算符只能出现一次(如果 - 是前缀否定,则不允许--2)。
但是,我想解析这些字符串。
具体而言,请考虑以下语法:
sentence:
| identifier
| "~" sentence
| sentence & sentence
| "!" sentence
运算符优先级为:"~"
绑定强于"&"
绑定强于"!"
例如,我想要句子
! ~a & b
被解析为
! ( (~a) & b )
和句子
~ ! a & b
as
~( ! ( a & b) )
Parsec允许我这样做(并指定运算符优先级),但是,我希望能够链接前缀,例如~ ~ ! ~ a
。
Parsec不允许这样做。
我找到了解决方案for chaining prefixes,但是这个解决方案不允许我为不同的前缀运算符指定不同的运算符优先级(“〜”和“!”绑定都强于“&”,或者没有他们确实)
有人有解决方案吗?
修改
使操作符绑定正确但不允许链接的部分解决方案: http://lpaste.net/143362
带链接的部分解决方案,但“〜”运算符绑定错误: http://lpaste.net/143364
修改:有关latest answer的更多说明。
我实际上希望&
是关联的。左或右无所谓。左与右关联性仅在具有相同优先级的运算符之间起作用。
对于您的示例,通过注意&
绑定强于!
(&
具有更高的运算符优先级)来解决所有问题
因此,你担心的表达方式是:
a & ! b & c
应该成为:
(首先绑定&
)
a & ! (b & c)
同样,应该解析! a & ! b & c
(第一次绑定&)
! a & ! (b & c)
,! a & (! (b & c))
,! (a & (! (b & c)))
答案 0 :(得分:4)
我对原始答案不满意,因为它没有解决各种优先级的前缀和后缀运算符的一般情况,并且它要求程序员必须考虑语法而不是仅仅依赖{{1做正确的事。
我在网上搜寻并发现了Pratt method for recursive descent parsing of expressions。我能够实现一个替换buildExpressionParser
的紧凑型Haskell版本。它与buildExpressionParser
具有完全相同的接口,但不要求您使用链接前缀组合器或使用术语解析器。我玩你的语法,改变buildExpressionParser
的关联性,并将前缀运算符切换到后缀运算符,这一切似乎都有用......
&
答案 1 :(得分:2)
http://lpaste.net/143362部分解决方案的一个问题是它无法识别~ ! a
。
但是,如果将操作员表更改为:
table = [ [ Prefix tilde ]
, [ Infix amper AssocLeft ]
, [ Prefix bang ]
, [ Prefix tilde ]
]
它可以正确地解析该表达式以及! ~a & b
,~ ! a & b
。代码位于:http://lpaste.net/143370
所以现在将这个想法与你的链接结合起来并尝试:
table = [ [ Prefix (chained tilde) ]
, [ Infix amper AssocLeft ]
, [ Prefix (chained bang) ]
, [ Prefix (chained tilde) ]
]
chained p = chainl1 p $ return (.)
答案 2 :(得分:2)
你想要的解析器的左因子语法是:
public void handle(HttpChannel connection) throws IOException,
ServletException {
super.handle(connection);
connection.getRequest().getTimeStamp();
}
可以在EBNF中重写为:
sentence : '!' sentence
| sentence1
sentence1 : sentence2 '&' sentence1
| sentence2
sentence2 : '~' sentence2
| term
term : '!' sentence
| ident
由sentence : '!'* sentence1
sentence1 : sentence2 ('&' sentence2)*
sentence2 : '~'* term
term : '!' sentence
| ident
使用链式前缀运算符生成的解析器几乎生成此解析器,除了它在术语解析器中不包含buildExpressionParser
规则;因此在!
之后遇到!
时出现解析错误。
鉴于以下内容:
~
我们可以手动定义{-# LANGUAGE NoMonomorphismRestriction #-}
module Main where
import Control.Monad
import Text.Parsec
import Text.Parsec.Expr
import Text.Parsec.Char
import Control.Applicative ( (<*), (*>), (<*>), (<$), (<$>) )
data Sentence = Tilde Sentence
| Bang Sentence
| Amper Sentence Sentence
| Ident String
deriving ( Eq, Ord, Show )
bangP = Bang <$ lexeme (char '!')
amperP = Amper <$ lexeme (char '&')
tildeP = Tilde <$ lexeme (char '~')
identP = Ident <$> lexeme (many1 alphaNum)
lexeme = (<* spaces)
parser = spaces *> sentence <* eof
main = do
let inputs = [ "a", "! a", "~ a", "a & b", "! a & b"
, "~ a & b", "! ~ a & b", "~ ! a & b", "! ~ ! a"
, "~ a & b", "a & ! b & c & d"
]
forM_ inputs $ \input -> do
putStr input
putStr " -> "
parseTest parser input
解析器:
sentence
如果我们将sentence = sentence0 where
sentence0 = chainl bangP (return (.)) id <*> sentence1
sentence1 = chainl1 sentence2 amperP
sentence2 = chainl tildeP (return (.)) id <*> term
term = (bangP <*> sentence0) <|> identP
规则添加到buildExpressionParser
解析器中,我们可以使用!
:
term
答案 3 :(得分:1)
新答案......
您是否考虑过&amp; 运算符的相关性?
这是我想出的另一个想法,假设&amp; 是正确关联的。
我认为&amp; 的相关性很重要,例如我们有:
a & ! b & c --> a & (! b & c) --> a & ! (b & c)
或
a & ! b & c --> (a & (! b)) & c
要考虑的另一个案例是! a & ! b & c
- 您希望如何解析?
实施:
{-# LANGUAGE NoMonomorphismRestriction, FlexibleContexts #-}
import Text.Parsec
import Control.Monad
import Text.ParserCombinators.Parsec hiding (runParser, try)
import Text.Parsec.Char
data Sentence = Ident String | Bang Sentence | Tilde Sentence | Amper Sentence Sentence
deriving (Show)
lexer p = do x <- p; spaces; return x
ident = lexer (many1 letter)
sym ch = lexer (char ch)
tilde = sym '~'
bang = sym '!'
amper = sym '&'
parens p = between (sym '(') (sym ')') p
term = parens expr
<|> (fmap Ident ident)
<?> "simple expression"
prefixOps = many (try tilde <|> bang)
expr = do
ops <- fmap reverse prefixOps
lhs <- term
let (ops', lhs') = popTildes ops lhs
pre = mkPrefixNode ops'
mrhs <- try (fmap Just (amper >> expr)) <|> (return Nothing)
case mrhs of
Nothing -> return $ pre lhs'
Just rhs -> return $ pre (Amper lhs' rhs)
popTildes :: [Char] -> Sentence -> ([Char], Sentence)
popTildes ('~':rest) s = popTildes rest (Tilde s)
popTildes ops s = (ops, s)
mkPrefixNode :: [Char] -> (Sentence -> Sentence)
mkPrefixNode [] = id
mkPrefixNode ('~':rest) = mkPrefixNode rest . Tilde
mkPrefixNode ('!':rest) = mkPrefixNode rest . Bang
mkPrefixNode _ = error "can't happen"
check :: String -> IO ()
check input = do
let padded = input ++ (replicate (15-length input) ' ')
case parse expr "-" input of
Left e -> do putStrLn $ "FAILED: " ++ input
putStrLn $ " " ++ show e
Right x -> do putStrLn $ "OK: " ++ padded ++ " -> " ++ show x
inputs = [ "a", "! a", "~ a", "a & b", "! a & b", "~ a & b", "! ~ a & b"
, "~ ! a", "! ~a & b", "~ ! a & b ", "! ~ ! a 2"
]
main = mapM_ check inputs