使用JavaScript实现一个“字节码解释器”,并用它重新实现JS科学计算器的后端(后续3)中间休息思考

反省一下我目前的字节码指令设计:

1、CallPrimitiveFunction是基于寄存器的,有一个最简单的ABI,但中间的临时变量却是需要Push/Pop的,什么时候需要将原语函数的计算结果Push,似乎没有一个好的指导原则?

2、嗯,当前parser是基于算符优先+递归下降技术的,似乎可以这样:

考虑一般的子表达式:

subExpr1 op subExpr2

其中,子表达式有可能递归且包含了嵌套原语函数操作,有可能仅仅是一个常量(但目前的语法不支持直接识别常量)

/*
   //BNF定义:
   //exprN代表优先级>=N的算符表达式
   expr := expr100 | expr80 | expr60 | expr40 | ... | expr20
   expr100 := value //数值常量优先级最高
   expr80  :=  ( expr ) | expr100 //其次是括号表达式
   expr60  :=  sin expr60 | cos expr60 | ln expr60 | log expr60 | sqrt2 | ... | expr80 
      //其次是一元函数, 一元取负暂不考虑
      //TODO: 这里可能需要插入pow求幂运算;
   expr50  := expr60 x^y expr50 | expr60 
      //二元函数的优先级高于乘除运算,低于一元函数,不加括号的情况下,二元函数从右往左运算;
   expr40  :=  expr50 '*' expr50 | expr50 '/' expr50  | expr50 //接下来是二元乘除运算
      ==> expr60 | expr60 ( '*' expr40 )* | expr60 ( '/' expr40)*
   expr20  := expr20 + expr20 | expr20 - expr40  | expr40 //接下来是二元加减运算
      ==> expr40 | expr40 ('+' expr20)* | expr40 ('-' expr40)* 
      //注意,减法运算不满足交换律,右边的被减数优先级必须至少是乘和除
   //糟糕的问题:expr40、expr20正常的写法会导致左递归,需要改写
  
  //同等优先级的情况下看结合性,不过一般情况下都是左结合,即从左往右运算,假如要考虑sin cos 1(不加括号),则可以认为一元函数是右结合的
*/

数值常量expr100的优先级很高,但它并不直接包含在expr{20-60}中。也就是说,即使并不是包含了新的运算嵌套子表达式,在我的parser流程中也是存在递归的。

对于这种情况,实际上最终的常量可以用一个MovImm #num, R0,并不需要Push R0压栈。而如果是包含了嵌套运算的情况呢,由于简单的ABI规定R0必须用来传参,所以当前的R0就必须压栈了。

问题倒也不大,问题是要有一致的规范,最好还能够避免不必要的Push/Pop

有个简单的处理:在递归处理子表达式时,假设有约束

1、调用子表达式之后,栈顶指针跟之前应该是一样的;
2、调用子表达式之后,子表达式的结果应该在R0里面

因此,有推论:

1、调用子表达式之前,必须Push R0(不需要关心R1)
1.1 但子表达式就是顶级的话,实际上不需要Push R0

在我前面定义的文法中,如果遇到了expr100,在生成了 MovImm #num, R0 之后,到底需要不需要再Push R0压栈呢?由于缺少上下文,expr100本身应该不用关心这个问题,由解析器的上下文,即expr{20-80} 4个过程做判断。

是否可以认为,Pop的作用仅仅为PrimitiveFunction提供调用参数?


我还没想好,暂时就先这样吧。

你可能感兴趣的:(JavaScript,字节码,编译器,解释器,指令生成)