Results 1 to 6 of 6

Thread: Parser for mathematic expression from QString

  1. #1
    Join Date
    Mar 2016
    Posts
    22
    Qt products
    Qt5
    Platforms
    Unix/X11 Windows

    Default Parser for mathematic expression from QString

    Hello!
    I try to use a parser for simple math expressions from a QString, as 3+2.(5^2)/9....and so on (no variables to calculate, just numbers). I started with a recursive-descent parser, but then I found a simpler solution with QJSEngine:
    Qt Code:
    1. #include "math.h"
    2. #include "expression_calc.h"
    3. #include "ui_expression_calc.h"
    4. #include "QDebug"
    5. #include <QApplication>
    6. #include <QJSEngine>
    7. #include <QJSValue>
    8. #include <QObject>
    9.  
    10.  
    11. Expression_calc::Expression_calc(QWidget *parent) :
    12. QMainWindow(parent),
    13. ui(new Ui::Expression_calc)
    14. {
    15. ui->setupUi(this);
    16.  
    17. }
    18.  
    19. Expression_calc::~Expression_calc()
    20. {
    21. delete ui;
    22. }
    23.  
    24. void Expression_calc::on_CalcButton_clicked()
    25. {
    26. QString expression= ui->exp->text();
    27. QJSEngine parsexpression;
    28. double result=parsexpression.evaluate(expression).toNumber();
    29. ui->Result->setText(QString::number(result));
    30. }
    To copy to clipboard, switch view to plain text mode 
    Ok, very simply, ...but this method can interpret just +,-,*,/,()...
    I decide to manipulate the main QString expression with a
    Qt Code:
    1. expression.replace("sqrt","Math.sqrt");
    2. expression.replace("abs","Math.abs");
    3. expression.replace("cos","Math.cos");
    4. ......
    To copy to clipboard, switch view to plain text mode 
    Not very elegant, but a fast solution. In add, as I have to store the expressions, I can save the corrected versions.
    All it works, except for the damned "^". Math requires "pow(3,2)" instead of common expression 3^2, does someone have a fast solution to workaround this, or you suggest to return to a recursive-descent parser?
    Thanks!

  2. #2
    Join Date
    Jan 2008
    Location
    Alameda, CA, USA
    Posts
    5,230
    Thanks
    302
    Thanked 864 Times in 851 Posts
    Qt products
    Qt5
    Platforms
    Windows

    Default Re: Parser for mathematic expression from QString

    Write yourself a QRegularExpression string match that pre-processes your input string, looking for "expr1^expr2" and substitutes "pow( expr1, expr2)" in the same way as you replace your other expressions.

    Does the JS parser recognize "**" for exponentiation? That could be an even simpler search and replace.
    <=== The Great Pumpkin says ===>
    Please use CODE tags when posting source code so it is more readable. Click "Go Advanced" and then the "#" icon to insert the tags. Paste your code between them.

  3. #3
    Join Date
    Mar 2016
    Posts
    22
    Qt products
    Qt5
    Platforms
    Unix/X11 Windows

    Default Re: Parser for mathematic expression from QString

    I tried with "**" , hoping to have the same behavior as Java... but it didn't work. But your suggestion seems to be the right way, I'll try it.
    Thank you very much!

  4. #4
    Join Date
    Mar 2016
    Posts
    22
    Qt products
    Qt5
    Platforms
    Unix/X11 Windows

    Default Re: Parser for mathematic expression from QString

    I followed your suggestion, and I solved my problems!
    Perhaps, with more knowledge, I could do better, but for now it's ok. The main problem is that RegularExpression considers "+" as a part of a number, and also the hierarchy of function didn't work well, so I "frozen" the operators (changing them with PLUS, MINUS, etc.), unfreezing them when I need... By now it goes correctly for the main functions, I have to implement the trigonometric and ABS ones, (now, as program simplifies expression between brackets, "ABS(-5)" becomes "ABS-5" ->nan), but it shouldn't be a great problem.

    I found a useful help using https://regex101.com/, it's simpler to understand QRegularEspression's syntax than compiling the code each time

    This is the code:
    Qt Code:
    1. #include "math.h"
    2. #include "expression_calc.h"
    3. #include "ui_expression_calc.h"
    4. #include "QDebug"
    5. #include <QApplication>
    6. #include <QJSEngine>
    7. #include <QJSValue>
    8. #include <QObject>
    9.  
    10. Expression_calc::Expression_calc(QWidget *parent) :
    11. QMainWindow(parent),
    12. ui(new Ui::Expression_calc)
    13. {
    14. ui->setupUi(this);
    15.  
    16. }
    17.  
    18. Expression_calc::~Expression_calc()
    19. {
    20. delete ui;
    21. }
    22.  
    23. void Expression_calc::on_CalcButton_clicked()
    24. {
    25. QString expression= ui->exp->text();
    26. QString exp1,exp2;
    27. expression.replace("sin","Math.sin");
    28. expression.replace("cos","Math.cos");
    29. expression.replace("tan","Math.tan");
    30. //.... and so on for trigonometrical functions)
    31.  
    32. expression.replace("sqrt","Math.sqrt");
    33. expression.replace("(","[");
    34. expression.replace(")","]");
    35. expression.replace("^","POW");
    36. expression.replace("+","PLUS");
    37. expression.replace("-","MINUS");
    38. expression.replace("*","MULT");
    39. expression.replace("/","DIV");
    40.  
    41. while (expression.contains("[")) //START SOLVING EXPRESSIONS BETWEEN BRACKETS
    42. {
    43.  
    44. QRegularExpression rep("\\[([^\\]]+)\\]");
    45. QRegularExpressionMatch matchp = rep.match(expression);
    46. QString expp_w_brackets = matchp.captured(0); //original expression with brakets
    47. QString expp = matchp.captured(1);//expression without brakets
    48. expp.replace ("PLUS","+");
    49. expp.replace ("MINUS","-");
    50. expp.replace("MULT","*");
    51. expp.replace("DIV","/");
    52.  
    53. QJSEngine parsexpressionp;
    54. double resultp=parsexpressionp.evaluate(expp).toNumber();
    55. QString BraketResult=(QString::number(resultp));
    56. expression.replace(""+expp_w_brackets+"" ,""+BraketResult+"");
    57. }
    58.  
    59. while (expression.contains("POW")) //SOLVE POE EXPRESSION
    60. {
    61. QRegularExpression re1("(\\d+.\\d+POW)|(\\d+POW)");
    62. QRegularExpressionMatch match1 = re1.match(expression);
    63.  
    64. QRegularExpression re2("(POW\\d+.\\d+)|(POW\\d+)");
    65. QRegularExpressionMatch match2 = re2.match(expression);
    66.  
    67.  
    68. if (match1.hasMatch())
    69. {
    70. exp1 = match1.captured(0);
    71. exp1.replace("POW","");
    72. }
    73.  
    74. if (match2.hasMatch())
    75. {
    76. exp2 = match2.captured(0);
    77. exp2.replace("POW","");
    78. }
    79.  
    80.  
    81. expression.replace (""+exp1+"" "POW"""+exp2+"" , "Math.pow(" ""+exp1+"" "," ""+exp2+"" ")");
    82.  
    83. }
    84. expression.replace("[","(");
    85. expression.replace("]",")");
    86. expression.replace ("PLUS","+");
    87. expression.replace ("MINUS","-");
    88. expression.replace("MULT","*");
    89. expression.replace("DIV","/");
    90. QJSEngine parsexpression;
    91. double result=parsexpression.evaluate(expression).toNumber();
    92. ui->Result->setText(QString::number(result));
    93. }
    To copy to clipboard, switch view to plain text mode 
    Last edited by sr1s; 8th May 2018 at 11:30.

  5. #5
    Join Date
    Mar 2016
    Posts
    22
    Qt products
    Qt5
    Platforms
    Unix/X11 Windows

    Default Re: Parser for mathematic expression from QString

    I have to correct a mistake (if I had "2^(6/3)", the code returns a nan), in a second time I'll create a pow() function to have a more clear code...
    For the problem abs(-1)->abs1, or cos(0) -> cos0, a very rough solution: I change, for example, abs(-1) into math.abs{-1), and then, before processing expression, replace again "{" with "(".. Very ugly, but it's enough to avoid the transforming into math.abs-1...
    So, the code is this:
    Qt Code:
    1. #include "math.h"
    2. #include "expression_calc.h"
    3. #include "ui_expression_calc.h"
    4. #include "QDebug"
    5. #include <QApplication>
    6. #include <QJSEngine>
    7. #include <QJSValue>
    8. #include <QObject>
    9.  
    10. Expression_calc::Expression_calc(QWidget *parent) :
    11. QMainWindow(parent),
    12. ui(new Ui::Expression_calc)
    13. {
    14. ui->setupUi(this);
    15.  
    16. }
    17.  
    18. Expression_calc::~Expression_calc()
    19. {
    20. delete ui;
    21. }
    22.  
    23. void Expression_calc::on_CalcButton_clicked()
    24. {
    25. QString expression= ui->exp->text();
    26. QString exp1,exp2;
    27.  
    28. expression.replace("pi","Math.PI");
    29. expression.replace("abs(","Math.abs{");
    30. expression.replace("sin(","Math.sin{");
    31. expression.replace("cos(","Math.cos{");
    32. expression.replace("tan","Math.tan");
    33. //.... and so on for trigonometrical functions)
    34.  
    35. expression.replace("sqrt","Math.sqrt");
    36. expression.replace("(","[");
    37. expression.replace(")","]");
    38. expression.replace("^","POW");
    39. expression.replace("+","PLUS");
    40. expression.replace("-","MINUS");
    41. expression.replace("*","MULT");
    42. expression.replace("/","DIV");
    43.  
    44. while (expression.contains("[")) //START SOLVING EXPRESSIONS BETWEEN BRACKETS
    45. {
    46.  
    47. QRegularExpression rep("\\[([^\\]]+)\\]");
    48. QRegularExpressionMatch matchp = rep.match(expression);
    49. QString expp_w_brackets = matchp.captured(0); //original expression with brakets
    50. QString expp = matchp.captured(1);//expression without brakets
    51.  
    52. while (expp.contains("POW")) //SOLVE POE EXPRESSION
    53. {
    54. QRegularExpression re1("(\\d+.\\d+POW)|(\\d+POW)");
    55. QRegularExpressionMatch match1 = re1.match(expp);
    56.  
    57. QRegularExpression re2("(POW\\d+.\\d+)|(POW\\d+)");
    58. QRegularExpressionMatch match2 = re2.match(expp);
    59.  
    60. if (match1.hasMatch())
    61. {
    62. exp1 = match1.captured(0);
    63. exp1.replace("POW","");
    64. }
    65.  
    66. if (match2.hasMatch())
    67. {
    68. exp2 = match2.captured(0);
    69. exp2.replace("POW","");
    70. }
    71.  
    72. expp.replace (""+exp1+"" "POW"""+exp2+"" , "Math.pow(" ""+exp1+"" "," ""+exp2+"" ")");
    73. }
    74.  
    75.  
    76. expp.replace ("PLUS","+");
    77. expp.replace ("MINUS","-");
    78. expp.replace("MULT","*");
    79. expp.replace("DIV","/");
    80.  
    81. QJSEngine parsexpressionp;
    82. double resultp=parsexpressionp.evaluate(expp).toNumber();
    83. QString BraketResult=(QString::number(resultp));
    84. expression.replace(""+expp_w_brackets+"" ,""+BraketResult+"");
    85. }
    86.  
    87. while (expression.contains("POW")) //SOLVE POE EXPRESSION
    88. {
    89. QRegularExpression re1("(\\d+.\\d+POW)|(\\d+POW)");
    90. QRegularExpressionMatch match1 = re1.match(expression);
    91.  
    92. QRegularExpression re2("(POW\\d+.\\d+)|(POW\\d+)");
    93. QRegularExpressionMatch match2 = re2.match(expression);
    94.  
    95.  
    96. if (match1.hasMatch())
    97. {
    98. exp1 = match1.captured(0);
    99. exp1.replace("POW","");
    100. }
    101.  
    102. if (match2.hasMatch())
    103. {
    104. exp2 = match2.captured(0);
    105. exp2.replace("POW","");
    106. }
    107.  
    108.  
    109. expression.replace (""+exp1+"" "POW"""+exp2+"" , "Math.pow(" ""+exp1+"" "," ""+exp2+"" ")");
    110. }
    111.  
    112. expression.replace("{","(");
    113. expression.replace("[","(");
    114. expression.replace("]",")");
    115. expression.replace ("PLUS","+");
    116. expression.replace ("MINUS","-");
    117. expression.replace("MULT","*");
    118. expression.replace("DIV","/");
    119. QJSEngine parsexpression;
    120. double result=parsexpression.evaluate(expression).toNumber();
    121. ui->Result->setText(QString::number(result));
    122. }
    To copy to clipboard, switch view to plain text mode 
    Last edited by sr1s; 8th May 2018 at 16:37.

  6. #6
    Join Date
    Jan 2008
    Location
    Alameda, CA, USA
    Posts
    5,230
    Thanks
    302
    Thanked 864 Times in 851 Posts
    Qt products
    Qt5
    Platforms
    Windows

    Default Re: Parser for mathematic expression from QString

    Well, the more possibilities you add, the more difficult it will be to keep the code from getting ugly. At some point I think you may have to consider a recursive descent parser - you'll get into problems of precedence, embedded expressions (like "2^(6/3)"), etc. where the work of getting them suitable for the JS parser is as much as evaluating it yourself.

    Here is a simple one in C# which looks like it can be translated into C++ and modified pretty easily to support functions and precedence. Or you can go old-school and use Flex / Bison or Flex++ / Bison++. You'll learn more than you ever wanted to know about regular expressions.
    <=== The Great Pumpkin says ===>
    Please use CODE tags when posting source code so it is more readable. Click "Go Advanced" and then the "#" icon to insert the tags. Paste your code between them.

Similar Threads

  1. parser
    By mickey in forum General Discussion
    Replies: 2
    Last Post: 23rd September 2009, 12:18
  2. Parser
    By ComaWhite in forum General Programming
    Replies: 4
    Last Post: 28th October 2008, 23:22
  3. Mathematic surfers
    By Colx007 in forum Qt Programming
    Replies: 1
    Last Post: 20th May 2008, 21:57
  4. How to get a QString from a file (use regular expression)
    By fengtian.we in forum Qt Programming
    Replies: 16
    Last Post: 31st May 2007, 11:06
  5. Parser in Qt.... HELP!
    By deepusrp in forum Newbie
    Replies: 6
    Last Post: 23rd January 2007, 21:18

Bookmarks

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  
Digia, Qt and their respective logos are trademarks of Digia Plc in Finland and/or other countries worldwide.