Haskell 中有理数和算术表达式之间的二元运算解析器
Parser for binary operations between Rationals and Arithmetic expressions in Haskell
受到以下 project 的启发,我正在使用线性表达式并定义了以下结构和解析器。
data AExp
= Lit Rational
| Var String
| AExp :+: AExp
| Rational :*: AExp
deriving (Eq)
import Text.Parsec
import Text.Parsec.Char
import Text.Parsec.Expr
import Text.Parsec.Language (javaStyle)
import Text.Parsec.String
import Control.Monad (void, ap)
import qualified Text.Parsec.Token as Token
Token.TokenParser {..} = Token.makeTokenParser javaStyle
binary name fun = Infix (fun <$ reservedOp name) AssocLeft
whitespace :: Parser ()
whitespace = void $ many $ oneOf " \n\t"
regularParse :: Parser a -> String -> Either ParseError a
regularParse p = parse p ""
rational :: Parser Rational
rational = do
whitespace
num <- many1 digit
void $ char '/'
den <- many1 digit
whitespace
return $ toRational $ (read num)/ (read den)
aexp :: Parser AExp
aexp = buildExpressionParser table term
where term = Lit <$> rational
<|> Var <$> identifier
<|> try ((:*:) <$> (rational <* reservedOp "*") <*> aexp)
<|> try (parens aexp)
table = [ [ binary "+" (:+:)]]
我的问题是乘法 (:*:) 和两种不同类型之间的一般二元运算 (Rational - AExp)。以下示例显示了我的结果。
Main>regularParse aexp "10/1 * x"
Right (Lit 10 % 1)
即不匹配乘法(*),匹配字面量变量Lit
我找了一些表达式解析器的例子,但是我总是发现其中的乘法是AExp之间的二元运算,即结构和解析器是这样的:
data AExp
= Lit Rational
| Var String
| AExp:+: AExp
| AExp:*: AExp
deriving (Eq)
aexp :: Parser AExp
aexp = buildExpressionParser table term
where term = Lit <$> rational
<|> Var <$> identifier
<|> try ((:*:) <$> (rational <* reservedOp "*") <*> aexp)
<|> try (parens aexp)
table = [[binary "+" (:+:)],
[binary "*" (:*:)]]
我关注的项目就是这样定义的
我可以尝试将另一个重点放在解析器上,但对我来说,遵循项目指南更容易,因为我的许多结构都非常相似。
我如何定义乘法解析器 (:*:) 或者有任何示例可以指导我吗?
提前致谢
问题出在这里:
term = Lit <$> rational
<|> ...
<|> try ((:*:) <$> (rational <* reservedOp "*") <*> aexp)
<|> ...
这将首先尝试解析一个单独的 rational
;如果成功,将不会尝试剩余的分支。选择这种承诺第一次成功解析的策略是为了提高效率。只需更改顺序即可帮助您克服困难,并解决您的下一个问题(总是有一个,不是吗??)。
term = try ((:*:) <$> (rational <* reservedOp "*") <*> aexp)
<|> Lit <$> rational
<|> ...
我对我原来的问题想得更多了。我想为线性表达式制作一个解析器。
一个有限但足够的解决方案是:
varAExp :: Parser AExp
varAExp = do
x <- identifier
return $ Var x
aexp :: Parser AExp
aexp = buildExpressionParser table term
where term = try ((:*:) <$> (rational <* reservedOp "*") <*> varAExp)
<|> Lit <$> rational
<|> Var <$> identifier
<|> try (parens aexp)
table = [[binary "+" (:+:) ]]
有了这个解析器,我可以处理这种类型的表达式:
arit_1 = regularParse aexp "10/1*x + 3/2*y + 1/1 + 3/1"
我无法表达数字的乘法,但正如我之前所说,这对我来说已经足够了。
感谢您的帮助
受到以下 project 的启发,我正在使用线性表达式并定义了以下结构和解析器。
data AExp
= Lit Rational
| Var String
| AExp :+: AExp
| Rational :*: AExp
deriving (Eq)
import Text.Parsec
import Text.Parsec.Char
import Text.Parsec.Expr
import Text.Parsec.Language (javaStyle)
import Text.Parsec.String
import Control.Monad (void, ap)
import qualified Text.Parsec.Token as Token
Token.TokenParser {..} = Token.makeTokenParser javaStyle
binary name fun = Infix (fun <$ reservedOp name) AssocLeft
whitespace :: Parser ()
whitespace = void $ many $ oneOf " \n\t"
regularParse :: Parser a -> String -> Either ParseError a
regularParse p = parse p ""
rational :: Parser Rational
rational = do
whitespace
num <- many1 digit
void $ char '/'
den <- many1 digit
whitespace
return $ toRational $ (read num)/ (read den)
aexp :: Parser AExp
aexp = buildExpressionParser table term
where term = Lit <$> rational
<|> Var <$> identifier
<|> try ((:*:) <$> (rational <* reservedOp "*") <*> aexp)
<|> try (parens aexp)
table = [ [ binary "+" (:+:)]]
我的问题是乘法 (:*:) 和两种不同类型之间的一般二元运算 (Rational - AExp)。以下示例显示了我的结果。
Main>regularParse aexp "10/1 * x"
Right (Lit 10 % 1)
即不匹配乘法(*),匹配字面量变量Lit
我找了一些表达式解析器的例子,但是我总是发现其中的乘法是AExp之间的二元运算,即结构和解析器是这样的:
data AExp
= Lit Rational
| Var String
| AExp:+: AExp
| AExp:*: AExp
deriving (Eq)
aexp :: Parser AExp
aexp = buildExpressionParser table term
where term = Lit <$> rational
<|> Var <$> identifier
<|> try ((:*:) <$> (rational <* reservedOp "*") <*> aexp)
<|> try (parens aexp)
table = [[binary "+" (:+:)],
[binary "*" (:*:)]]
我关注的项目就是这样定义的
我可以尝试将另一个重点放在解析器上,但对我来说,遵循项目指南更容易,因为我的许多结构都非常相似。
我如何定义乘法解析器 (:*:) 或者有任何示例可以指导我吗?
提前致谢
问题出在这里:
term = Lit <$> rational
<|> ...
<|> try ((:*:) <$> (rational <* reservedOp "*") <*> aexp)
<|> ...
这将首先尝试解析一个单独的 rational
;如果成功,将不会尝试剩余的分支。选择这种承诺第一次成功解析的策略是为了提高效率。只需更改顺序即可帮助您克服困难,并解决您的下一个问题(总是有一个,不是吗??)。
term = try ((:*:) <$> (rational <* reservedOp "*") <*> aexp)
<|> Lit <$> rational
<|> ...
我对我原来的问题想得更多了。我想为线性表达式制作一个解析器。 一个有限但足够的解决方案是:
varAExp :: Parser AExp
varAExp = do
x <- identifier
return $ Var x
aexp :: Parser AExp
aexp = buildExpressionParser table term
where term = try ((:*:) <$> (rational <* reservedOp "*") <*> varAExp)
<|> Lit <$> rational
<|> Var <$> identifier
<|> try (parens aexp)
table = [[binary "+" (:+:) ]]
有了这个解析器,我可以处理这种类型的表达式:
arit_1 = regularParse aexp "10/1*x + 3/2*y + 1/1 + 3/1"
我无法表达数字的乘法,但正如我之前所说,这对我来说已经足够了。 感谢您的帮助