0

I have a task where I need to convert equations in one language (MATLAB) to another (C++) using Python. Everything is straightforward except the power operation. The equations are represented by Python strings.

For example, I want to convert

Eq = ((a+b)^2 + (c+d)^(1/2)) * e

to,

Eq = (pow((a+b),2) + pow(c+d, 1/2)) * e

I tried regular expression, but it does not seem to work because of nested parenthesis. Is there any good way to do this?

ysakamoto
  • 2,512
  • 1
  • 16
  • 22
  • In C, `pow(c+d, 1/2)` is always 1. So it's not quite as simple as just changing operators. – rici May 12 '14 at 04:30
  • Do you want to convert the first string to second string? Your question is not clear. I am close-voting it. – thefourtheye May 12 '14 at 04:32
  • It has been covered many times why using regex to parse structured data is a bad idea. Usually people try it with [HTML](http://stackoverflow.com/questions/1732348/regex-match-open-tags-except-xhtml-self-contained-tags/1732454#1732454) so this is slightly novel. But it's still a bad idea. – tripleee May 12 '14 at 04:40
  • This is the kind of task that [source-to-source compilers](https://en.wikipedia.org/wiki/Source-to-source_compiler) are designed to handle. – Anderson Green May 12 '14 at 04:40
  • @thefourtheye yes I want to convert the first string to the second. I edited my question. – ysakamoto May 12 '14 at 04:42
  • To expound on @rici's comment, `1/2` evaluates to `0` in C, you need to convert integer literals to floating point literals in expressions, as `1.0/2.0`. – U2EF1 May 12 '14 at 04:54

3 Answers3

2

The best way to solve this would be to use Abstract Syntax Tree of Python. We can get that using ast module.

import ast, _ast

ops = {_ast.Mult: "*", _ast.Add: "+", _ast.BitXor: "pow", _ast.Div: "/"}

def rec(n):
    if isinstance(n, _ast.Expr):
        return rec(n.value)
    elif isinstance(n, _ast.BinOp):
        if isinstance(n.op, _ast.BitXor):
            return "{}({}, {})".format(ops[type(n.op)], rec(n.left),rec(n.right))
        else:
            return "({} {} {})".format(rec(n.left), ops[type(n.op)],rec(n.right))
    elif isinstance(n, _ast.Name):
        return n.id
    elif isinstance(n, _ast.Num):
        return n.n

print rec(ast.parse("(((a+b)^2) + ((c+d)^(1/2))) * e").body[0])
# ((pow((a + b), 2) + pow((c + d), (1 / 2))) * e)

Note: Since ^ means Binary XOR operation in Python, you need to enclose that expression with an extra parenthesis, like I have shown in the example.

First, we check if it is an expression, then we process its value. If it is a binary operation, we check of it is Bitwise XOR, in that case we return the result in a different format, otherwise we return the result like left op right. If the current item looks like a name, then we return its id or if it is a number we return the attribute n.

thefourtheye
  • 233,700
  • 52
  • 457
  • 497
1

You can do it as follows. Note: This is still a very primitive approach and I'd be careful about relying on it 100%:

import re

s = "Eq = ((a+b)^2 + (c+d)^(1/2)) * e"

>>> print re.sub(r'\(([^()\s]*)\)\^\(?([^()\s]*)\)?', 'pow(\\1,\\2)', s)
Eq = (pow(a+b,2) + pow(c+d,1/2)) * e
sshashank124
  • 31,495
  • 9
  • 67
  • 76
0

I think I'd do this with a simple tokeniser and parser rather than regular expressions. It's not that hard to write and will be more robust and readable than a regular expression based parser.

Community
  • 1
  • 1
Noufal Ibrahim
  • 71,383
  • 13
  • 135
  • 169