找回密码
 快速注册
搜索
查看: 61|回复: 4

四则运算的句法分析器

[复制链接]

3149

主题

8386

回帖

6万

积分

$\style{scale:11;fill:#eff}꩜$

积分
65391
QQ

显示全部楼层

hbghlyj 发表于 2022-12-25 06:16 |阅读模式
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]*
复制代码

例子
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 🧮

3149

主题

8386

回帖

6万

积分

$\style{scale:11;fill:#eff}꩜$

积分
65391
QQ

显示全部楼层

 楼主| hbghlyj 发表于 2022-12-26 03:34
本帖最后由 hbghlyj 于 2023-1-1 14:26 编辑 按照这里所说
  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); }
复制代码

点评

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

17

主题

127

回帖

1822

积分

积分
1822

显示全部楼层

uk702 发表于 2022-12-26 09:08
本帖最后由 uk702 于 2022-12-26 09:17 编辑
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) 不知可不可以,相信在语法上是可以的,但能不能达到运算的目的,就不好说了。

3149

主题

8386

回帖

6万

积分

$\style{scale:11;fill:#eff}꩜$

积分
65391
QQ

显示全部楼层

 楼主| hbghlyj 发表于 2023-1-1 19:35
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

手机版|悠闲数学娱乐论坛(第3版)

GMT+8, 2025-3-4 15:41

Powered by Discuz!

× 快速回复 返回顶部 返回列表