Forgot password?
 Create new account
View 131|Reply 4

四则运算的句法分析器

[Copy link]

3147

Threads

8493

Posts

610K

Credits

Credits
66163
QQ

Show all posts

hbghlyj Posted at 2022-12-25 06:16:37 |Read mode
PEG.js demonstation有一个四则运算的句法分析器
  1. Expression
  2.   = head:Term tail:(_ ("+" / "-") _ Term)* {
  3.       return tail.reduce(function(result, element) {
  4.         if (element[1] === "+") { return result + element[3]; }
  5.         if (element[1] === "-") { return result - element[3]; }
  6.       }, head);
  7.     }
  8. Term
  9.   = head:Factor tail:(_ ("*" / "/") _ Factor)* {
  10.       return tail.reduce(function(result, element) {
  11.         if (element[1] === "*") { return result * element[3]; }
  12.         if (element[1] === "/") { return result / element[3]; }
  13.       }, head);
  14.     }
  15. Factor
  16.   = "(" _ expr:Expression _ ")" { return expr; }
  17.   / Integer
  18. Integer "integer"
  19.   = [0-9]+ { return parseInt(text(), 10); }
  20. _ "whitespace"
  21.   = [ \t\n\r]*
Copy the Code

例子
2 * (3 + 16/2/2)输出14

解释
  • Expression拆分成head和tail.
    head是一个Term.
    tail是几个前面带着+或-的Term(这里*表示0个或更多),每个+或-两边可以带空白(见最后一条).
  • Term拆分成head和tail.
    head是一个Factor.
    tail是几个前面带着*或/的Factor,每个*或/两边可以带空白.
  • Factor是一个用()括住的Expression(两边可以带空白),或一个Integer.
  • Integer是几个0-9字符(这里+表示1个或更多).
  • _是几个连续空白(0个或更多).

问题
如何给这个分析器加上乘方^呢? 像2^2=4、2^2^2=16这些.
注意, 乘方^是右结合的, 而*和/是左结合的.

相关
latex.js is written in JavaScript using PEG.js.
Wikipedia–Abstract syntax tree
Context-Free Grammars
$\LaTeX$ Calculator$^2$
Interpreted math equation language 🧮

Related threads

3147

Threads

8493

Posts

610K

Credits

Credits
66163
QQ

Show all posts

 Author| hbghlyj Posted at 2022-12-26 03:34:04
Last edited by hbghlyj at 2023-1-1 14:26:00按照这里所说
  E -> E+T | E-T | T
  T -> T*X | T/X | X
  X -> F^X | F //Note: it's F^X not X^F
  F -> i | (E)

写成PEG.js我想应该是(不含空白的话)
  1. E
  2.   = head:("-"?T) tail:(("+"/"-")T)* {
  3.       if(head[0]){ head[1] = - head[1]; };
  4.       return tail.reduce(function(result, element) {
  5.         if (element[0] === "+") { return result + element[1]; }
  6.         if (element[0] === "-") { return result - element[1]; }
  7.       }, head[1]);
  8.     }/T
  9. T
  10.   = head:X tail:(("*"/"/")X)* {
  11.       return tail.reduce(function(result, element) {
  12.         if (element[0] === "*") { return result * element[1]; }
  13.         if (element[0] === "/") { return result / element[1]; }
  14.       }, head);
  15.     }/X
  16. X
  17.   = head:F "^" tail:X { return head**tail; }/F
  18. F
  19.   = "("expr:E")"{return expr;}/Integer
  20. Integer "integer"
  21.   = [0-9]+ { return parseInt(text(), 10); }
Copy the Code

Comment

不懂,可以试试:X
  = F "^" X { return head^tail; }
  / F  Posted at 2022-12-26 08:56

17

Threads

126

Posts

1822

Credits

Credits
1822

Show all posts

uk702 Posted at 2022-12-26 09:08:59
Last edited by uk702 at 2022-12-26 09:17:00
hbghlyj 发表于 2022-12-26 03:34
按照这里所说
  E -> E+T | E-T | T
  T -> T*X | T/X | X
不好意思,上面的评论是错的。

我想这个分析器大概是无穷递降法:
E -> E+T | E-T | T
T -> T*X | T/X | X
F -> i | (E)

对每一个表达式,识别有没有关键符号,如 E->E+T | E-T | T,如果能找到+或-,则抽取左边部分为 E,右边部分为 T,如果没有关键符号,则整个表达式整体下降一级作为 T 继续解析。

@@当碰到 X -> F^X | F 时,如果找到 ^ 符号,则左边分解成 F ,右边分解成 X,但 X 无法下降,只能继续到当成 X -> F^X | F 来解析,这样就可能造成了无穷循环。@@
-- 好像也不对,当碰到 X -> F^X | F 时,如果找到 ^ 符号,则左边分解成 F ,右边分解成 X,然后继续到当成 X -> F^X | F,如果找到 ^,这时 X  分解成更小的 F^X,如果找不到 ^,那么跟其它规则一样,执行 x->F,将 X 整个下降一级,这么说这个语法应该也没问题呀。

如果换成 X -> F^P | F, F -> i | (E), P -> i | (E) 不知可不可以,相信在语法上是可以的,但能不能达到运算的目的,就不好说了。

3147

Threads

8493

Posts

610K

Credits

Credits
66163
QQ

Show all posts

 Author| hbghlyj Posted at 2023-1-1 19:35:08
uk702 发表于 2022-12-26 02:08
不好意思,上面的评论是错的。

我想这个分析器大概是无穷递降法:

把2#改好了. 仿照1#把tail写成数组, 就没有possible infinite loop了
测试:
-1+(1+2)^(2+2)输出80
-2^2+2^4输出12
2^2^2^2输出65536
-2^3+3^2输出1

手机版Mobile version|Leisure Math Forum

2025-4-20 22:18 GMT+8

Powered by Discuz!

× Quick Reply To Top Return to the list