1

Sorry if this is duplicated. I thought I'd reword my question a little bit.

How could I use regex to evaluate a mathematical expression? Without using the eval function.

Example expressions:

math1 = "1+1"
math2 = "3+2-1"

I would like it to work for a variable number of numbers in the expression like I showed in the example.

9
  • possible duplicate of Ruby evalute without eval? Commented Apr 14, 2013 at 0:25
  • So just numbers and addition/subtraction? Why don't you write a parser instead? Commented Apr 14, 2013 at 0:26
  • @ŁukaszNiemier yeah i know, its a slightly different question..thanks for checking up on me though bro Commented Apr 14, 2013 at 0:29
  • What about send, is that allowed? Commented Apr 14, 2013 at 0:30
  • Yeah. How could I use that? Commented Apr 14, 2013 at 0:30

4 Answers 4

2

This is just a bad idea. Regexp is not a parser, nor an evaluator.

Use a grammar to describe your expressions. Parse it with a formal parser like the lovely ruby gem Treetop. Then evaluate the abstract syntax tree (AST) produced by the parser.

Gosh, Treetop's arithmetic example practically gives you the solution for free.

Sign up to request clarification or add additional context in comments.

3 Comments

+1 "This is just a bad idea". +1 "Regexp is not a parser, nor an evaluator". -1 "Use a grammar to describe your expressions" - the examples given by the OP are regular, and hence a RegEx can properly describe this (simplistic) grammar, a formal parser could be seen as overkill. -1 "Treetop's arithmetic example practically gives you the solution for free." - And looking at that grammar, there's proof of overkill. 1+1-1-1=0 :-)
Ah come on, @Sepster. 99% of the time when someone wants to "parse expressions" they ultimately mean a variety of arithmetic operators with varying precedence and parenthesis to group -- which is not regular. It always ends in tears until they learn to parse. This is 50+ year old technology, we need to stop pulling out the wrong tool.
Agreed. But the question didn't say anything about "parsing expression", rather, it says in its title "evaluate an expression with regex" and in its body "How could I use regex to evaluate a mathematical expression?". Which is not to suggest I think that it should be done this way. Just that it can be done. And btw +1 because I've never seen Treetop before.
1

This is a little late, but I wrote a gem for evaluating arbitrary mathematical expressions (and it doesn't use eval internally): https://github.com/rubysolo/dentaku

Comments

0

For addition and subtraction, this should work

(?:(/d+)([-+]))+(/d+)

This means:

  • one or more digits, followed by exactly one plus or minus
  • the above can be repeated as many times as required (this is a non capturing group)
  • and then must end with one or more digits.

Note that each individual number and sign are captured in groups 1..n

So to evaluate, you could take captures 1 and 3, applying the sign from capture 2. Then apply the sign from capture 4 (if it exists) with the previous result and the number from capture 5 (which must exist if capture 4 exists) and so on...

So to evaluate, in psuedo code:

  i=1
  result=capture(i)
  loop while i <= (n-2) (where n is the capture count):
    If capture(i+1) == "-" // is subtraction 
       result = result - capture(i+2)
    Else // is addition 
       result = result + capture(i+2)
    End if
    i = i + 2
  End while

This is only going to work for simple addition and subtraction like in the examples you provided, as it relies on left to right associativity. As others have suggested, you'll probably need to properly parse anything more complex, eg by building a tree of nodes that can then be evaluated in the correct (depth-first?) order.

2 Comments

Well that will match it. What about evaluating it without eval?
@Tommy sorry misunderstood. Have updated the answer with an algorithm to do this.
0

This is really messy…

  math2 = "12+3-4"
  head, *tail = math2.scan(/(?<digits>\d+)(?<op>[\+\-\*\/])?/)
  .map{|(digits,op)| 
    [digits.to_i,op]
  }
  .reverse

  tail.inject(head.first){|sum,(digits,op)|
    op.nil? ? 
      digits : 
      digits.send(op,sum) 
    }
  # => 11

You should really consider a parser though.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.