There are lines:

string f1 = "sin(x)^2+662^2"; string f2 = "e^2+e^5"; 

After replacement operations, you must obtain:

 f1 == "Math.Pow(sin(x),2)+Math.Pow(662,2)" f2 == "Math.Exp(2)+Math.Exp(5)" 

My goal is to create a primitive math expression parser. As I understand it, I can accomplish my task really through Regex, but I cannot make a pattern for a replacement

  • 2
    According to the rules of the community, questions should not be reduced to the completion of tasks for the author. Give an example of your implementation and ask a question describing specific problems. - null
  • four
    You are trying to solve a difficult problem with unfit means. You need a parser for arithmetic expressions. - VladD
  • If you are given an exhaustive answer, mark it as correct (a daw opposite the selected answer). - Nicolas Chabanovsky

2 answers 2

To begin with, we build syntactic and lexical analyzers (parser and lexer). Take for this ANTLR4.

 grammar Calculator; /// Parser prog: expr+ ; expr : expr '^' expr # Pow | expr op=('+'|'-') expr # AddSub | expr op=('*'|'/') expr # MulDiv | VAL # Val | fun=('sin'|'cos') '(' expr ')' # Function | '(' expr ')' # Parens ; /// Lexer VAL : [0-9]+ ( '.' [0-9]+ )?; MUL : '*'; DIV : '/'; ADD : '+'; SUB : '-'; POW : '^'; SIN : 'sin'; COS : 'cos'; WS : (' '|'\r'|'\n') -> channel(HIDDEN); 

Next, using the Visitor pattern, we go around the syntax tree:

 public class CalculatorVisitor : CalculatorBaseVisitor<string> { private static readonly Dictionary<int, string> FunctionMap = new Dictionary<int, string> { { CalculatorParser.SIN, "Math.Sin" }, { CalculatorParser.COS, "Math.Cos" }, }; public override string VisitPow (CalculatorParser.PowContext context) { return "Math.Pow(" + Visit(context.expr(0)) + "," + Visit(context.expr(1)) + ")"; } public override string VisitMulDiv (CalculatorParser.MulDivContext context) { return Visit(context.expr(0)) + context.op.Text + Visit(context.expr(1)); } public override string VisitAddSub (CalculatorParser.AddSubContext context) { return Visit(context.expr(0)) + context.op.Text + Visit(context.expr(1)); } public override string VisitVal (CalculatorParser.ValContext context) { return context.VAL().GetText(); } public override string VisitFunction (CalculatorParser.FunctionContext context) { return FunctionMap[context.fun.Type] + "(" + Visit(context.expr()) + ")"; } public override string VisitParens (CalculatorParser.ParensContext context) { return "(" + Visit(context.expr()) + ")"; } } 

Finally, convert the string:

 const string input = "(1+1)^2*(2+2)^3+sin(10.5)^2"; Console.WriteLine(new CalculatorVisitor().Visit( new CalculatorParser(new CommonTokenStream( new CalculatorLexer(new AntlrInputStream(input)))).prog())); 

We get at the output:

 Math.Pow((1+1),2)*Math.Pow((2+2),3)+Math.Pow(Math.Sin(10.5),2) 

PS Do not kick with your feet, for the first time in my life I see ANTLR.

    As I understand to perform my task really through Regex

    Not. More precisely, not really. Previewing backwards should allow writing correct parsing, but, formally, such a task is not solved by regular expressions.

    My goal is to create a primitive math expression parser.

    Here it should be created. The expression is parsed in one pass through it. Exponentiation complicates the task a little if it is performed from right to left.

    C #

    If you are going to compile the resulting line programmatically as C # code (which is not good at all), then do the same, but as VB.NET code, it will not require replacements that you ask about.

    • C # supports \ G , so the tokinizer itself is written in several lines. - KoVadim
    • @KoVadim, tokenizer or parser? - Qwertiy
    • one
      Tokinaiser - the one that will sort into tokens. I never tried it on Sharpe, but I wrote it on Perl - it’s literally two lines for each type of token. (if we have numbers, arithmetic, brackets and functions - 8-9 lines of code). And then, with the usual reverse Polish notation, a little more code is ready. - KoVadim
    • @KoVadim, I'm not arguing about the tokenizer. But with the parser nothing good will work. You can break into tokens with one regular schedule (and without \ G), then go through them with a recursive function and build a tree (and without a Polish notation). He did it on a hat - it seems there were no problems :) - Qwertiy
    • making a tree you make the same Polish record, just do it recursively. And the reverse Polish notation is so good that it does everything without recursion. But this is a matter of taste and RAM with a processor. But how to share one regular season is certainly interesting, I guess, in principle, but I would like to look at this option. - KoVadim