Home > Blockchain >  Parser for binary operations between Rationals and Arithmetic expressions in Haskell
Parser for binary operations between Rationals and Arithmetic expressions in Haskell

Time:09-24

Inspired by the following project, I am working with linear expressions and I have defined the following structure and parser.

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 " " (: :)]]

My problem is multiplication (:*:) and in general binary operations between two different types (Rational - AExp). The following example shows my result.

Main>regularParse aexp "10/1 * x"
Right (Lit 10 % 1)

That is, it does not match with multiplication (*), it matches with the literal variable Lit.

I have looked for some examples of parser of expressions, but in which I always found the multiplication is a binary operation between AExp, that is, the structure and parser are of this style:

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 "*" (:*:)]]

The project I am following is defined in this way.

I could try to take another focus for the parser, but for me it is easier to follow the project guide, since many of my structures are very similar.

How could I define the multiplication parser (:*:) or is there any example with to guide me?

Thanks in advance

CodePudding user response:

The problem is here:

term =  Lit  <$> rational
       <|> ...
       <|> try ((:*:) <$> (rational <* reservedOp "*") <*> aexp)
       <|> ...

This will try parsing a lone rational first; if that succeeds, the remaining branches will not be attempted. This policy of committing to the first successful parse was chosen for efficiency. Just changing the order will get you over this hump and to whatever your next problem will be (there's always one, isn't there??).

term = try ((:*:) <$> (rational <* reservedOp "*") <*> aexp)
      <|> Lit <$> rational
      <|> ...

CodePudding user response:

I thought a bit more about my original problem. I want to make a parser for linear expressions. A limited, but sufficient solution is:

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 " " (: :) ]]

With this parser I can work with expressions of this type:

arit_1 = regularParse aexp "10/1*x   3/2*y   1/1   3/1"

I cannot express the multiplication of numbers, but as I said before it is enough for me. thanks for your help

  • Related