How to do such substitutions with AWK or sed or Perl?

awk, bash, perl, sed, shell

Solution

Regular expressions cannot do arbitrary nesting, nor can then do precedence and associativity. Parsers are required for that; however, you can get close enough by starting with this:

Perl:

while(<>) {
   s/(a\(\d+\))\^(\d+)/p_tensor($2,$1)/g;
   s/(a\((\d+)\))\*(a\((\d+)\))/tensor($1, $3)/g if $2 != $4;
   print;
}

which is close, and gets you a single level. The extra nesting can then be "faked" by adding additional recursively defined patterns that go to whatever max nesting depth you need (often not many...expressions are rarely 3-4 levels deep in practice, which may be fine for you).

Try it with:

echo "3*a(3)^2+6*a(1)^2*(5*a(2)^2-2*a(4))+6*a(2)*a(4)+6*a(1)*(-4*a(2)*a(3)+a(5))" | perl t.pl

or something similar.

Problem

Want to substitute multiplication symbol "*" with "tensor", and the power symbol "^" with "p_tensor" using following substitution rules: ``` a(k)^n --> p_tensor(n,a(k)) a(i)*a(j) --> tensor(a(i),a(j)), when i=/=j ``` But when the symbol "*" is between a number and a(i), such as 3*a(i), we should keep the symbol "*" as what it is. so for example, ``` 5*a(i)*a(j)*(a(k1)+3*a(k2)) --> 5*tensor(tensor(a(i),a(j)),a(k1)+3*a(k2)) a(i)^2*a(j)^2 --> tensor(p_tensor(2,a(i)),p_tensor(2,a(j))) ... ``` Now I want to reformat the following expression using AWK or sed or Perl: ``` 3*a(3)^2+6*a(1)^2*(5*a(2)^2-2*a(4))+6*a(2)*a(4)+6*a(1)*(-4*a(2)*a(3)+a(5)) ``` Any ideas how? The expected result after substitution should be ``` 3*p_tensor(2,a(3))+6*tensor(p_tensor(2,a(1)),(5*p_tensor(2,a(2))-2*a(4))+6*tensor(a(2),a(4))+6*tensor(a(1),(-4*tensor(a(2),a(3))+a(5)) ```

Original source