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.