JavaScript实现高级科学计算器库

代码不贴了,主要讲解一下思路。

   //BNF定义:
   //exprN代表优先级>=N的算符表达式
   expr := expr20
   expr100 := value //数值常量优先级最高,当然也可以把expr100合并到expr80,这样可以少写一个parseExpr100解析子函数
   expr80  :=  ( expr ) | expr100 //其次是括号表达式
   expr60  :=  sin expr60 | cos expr60 | ln expr60 | log expr60 | sqrt expr60 | ... | expr80 //其次是一元函数, 一元取负暂不考虑
   expr50  := expr60 x^y expr50 | expr60 //二元函数的优先级高于乘除运算,低于一元函数,不加括号的情况下,二元函数从右往左运算;
   expr40  :=  expr50 '*' expr50 | expr50 '/' expr50  | expr50 //接下来是二元乘除运算
      ==> expr50 | expr50 ( '*' expr40 )* | expr50 ( '/' expr40)*
   expr20  := expr20 + expr40 | expr20 - expr40  | expr40 //接下来是二元加减运算
      ==> expr40 | expr40 ('+' expr40)* | expr40 ('-' expr40)*
      //注意,减法运算不满足交换律,右边的被减数优先级必须至少是乘和除
   //糟糕的问题:expr40、expr20正常的写法会导致左递归,需要改写
function AdvancedCalculator(){
   //语法分析的原始输入流:
   this.tokens = [];//中缀带括号的, 3种语法分析输入单位:类型为String的(和)、类型为Number的value、类型为Object/String的运算符
   this.tokens_scan_index = 0;
   this.saved_tokens_scan_index_stack = [];
   this.value_buffer = [];
}
AdvancedCalculator.prototype = {
  emitButton: function(token){
      ...

1、首先是写出BNF,这里要点是,非终结符对应的子表达式具有不同的优先级,优先级低的涵盖了高的,同时:

正常情况下,优先级低的会嵌套调用优先级高的,如果有左递归(+-*/的情况),此时相当于算符是左结合的,也可能是直接的右递归,如expr50二元中缀运算表达式(或一元函数),此时相当于右结合了;

左递归在直接转换为递归下降的语法分析之前,需要引入空表达式,以一般地转换为右递归的形式。

2、文法分析可以简化:可以把计算器上的按钮直接对应于一个Token,它要么是运算符,如+-*/sin cos等等(括号作为特殊的运算符考虑);要么是数字包含点号。

2个运算符之间的数字序列可以直接拼接,然后直接用JS Number()就可以转换为一个double value。

3、错误处理可以简化:每输入一个Token,则将累计的Token序列解析求值一次,如果遇到错误,则说明输入有问题(或者不完整)

4、优先级最低的是expr20加减表达式,但是语法递归解析却又是从expr20开始的,注意括号表达式expr80,它将优先级最低的expr20提升为最高

5、JS按 ES5标准,var变量是函数作用域,不是文法作用域(没有ES6 let),因此循环里声明的var下次再用的话最好reset

6、JS可以用数组[]直接作为一个栈,而且注意其core API:unshift/shift/push/pop,却没有add/remove/insert这样的命名;

7、使用下面的代码来定义Token常量:

AdvancedCalculator.prototype = {
   //复杂的运算符定义为单独的Object:
   SQRT:  "Sqrt",
   SIN:  "Sin",
   COS:  "Cos",
   TAN: "Tan",
   COT: "Cot",
   LOG:  "Log", //以10为底
   LN:  "Ln", //以e为底
   POW:  "Pow",//x^y
   PI: Math.PI, //这是数值常量,不是运算符

这些所谓的常量其实是prototype上关联的属性对象,但是用起来很方便,==即可比较。

8、通过return和throw同时使用2种控制流,return false表示当前流位置解析为子表达式a失败,但可以尝试作为b来重新解析;throw则代表确信输入有错误,或者期望一个Token输入的时候流已经结束

9、返回多个值:直接返回一个[]数组对象result,第一个元素true/false代表成功失败,第二个则是result[0]==true时的value

这实际上是Erlang的习俗,当然,ES6里有方便的destructing可以用。

实际上可以考虑用ES6来写代码,然后再用自动化工具翻译为ES5?不过我这里写代码仍然受到了Java的影响,比如hasMoreTokens/nextToken什么的

TODO:这里的后端(即除去语法解析的表达式求值部分)逻辑写的比较简单,相当于直接对AST作eval递归调用,可以考虑下面2种改进:

(1)支持翻译为最终的一个完整的JS计算表达式——这里,如果遇到需要特别的自定义函数的话,可以写一个匿名函数(function(a,b){...})(x,y)这样,当然,正常的都可以直接映射为JS Math.xxx。

(2)尽管如此,以上的2种求值方法仍然是解释器的思路,而不是编译器的思路,可考虑如何将结果翻译为序列化的带临时变量的语句序列。。。

这种情况下,稍微复杂一点。麻烦的是如何处理二元表达式,如:(1+2)-(3+4),翻译为单个的前缀/后缀表达式行不通:因为即使可翻译为前缀的-+12+34,或者后缀的12+34+-,但后面的形式仍然无法序列化地简单的单遍遍历求值。

尝试将操作数和操作符分开?比如,(1+2)-(3+4)翻译为2个序列:操作数的1234、和操作符的++-。

问题还是一样的:前2个+运算产生的中间临时变量怎么处理?

对于编译器的思路而言,由于要引入中间临时变量,实际上,就需要像Scheme的CPS转换,或者是LLVM这样的高级虚拟机指令,如alloca分配局部变量什么的。对于这里的表达式求值而言,操作符序列里需要引入2条虚拟的指令:push/pop,用于在序列求值的时候操作另外一个运行时的临时变量栈。

不需要考虑其他的额外指令。临时(局部)变量对应的就是栈,而栈只需要push/pop控制访问即可。注意,表达式求值这种问题没有复杂的控制流,也没有动态堆分配的new变量,相当于通用的编程语言的语法分析而言要简单多了。

版权声明:本文为博主原创文章,未经博主允许不得转载。

时间: 2024-12-19 06:28:35

JavaScript实现高级科学计算器库的相关文章

html+css+js实现科学计算器

代码地址如下:http://www.demodashi.com/demo/13751.html 项目描述 纯html+css+js实现一个科学计算器,支持平方开方指数对数等基本函数,支持键盘输入,有简单和高级两种模式 文件结构 纯html+css+js实现,文件结构非常简单,就三个文件. 演示效果 实验设计 将按钮的value设置为按钮显示的字符,当点击按钮的时候,输入框增加的字符为按钮的value,其中函数的按钮增加的字符为最后一个x所在的位置前面的字符,即到左括号. <div id="

《团队-科学计算器-设计文档》

设计文档: 项目:科学计算器 编辑器:python 所运用知识: 1.字符串的处理 2.正则表达式的运用 3.函数递归 基本思路: 需要优先处理内层括号运算--外层括号运算--先乘除后加减的原则: 1.正则处理用户输入的字符串,然后对其进行判断,判断计算公式是否有括号,有就先将计算公式进行正则处理,先获取最里层的每一个数据,然后一一计算 2.把有括号的计算公式计算出来的结果替换原来初始公式的位置,计算之前分别对重复运算符进行处理需要处理的重复运算 3.然后依次从里到外去除括号并进行计算,和位置替

转载~如何在Pycharm中安装numpy等科学计算库

转载自机器小人z windows下如何快速优雅的使用python的科学计算库? Python是一种强大的编程语言,其提供了很多用于科学计算的模块,常见的包括numpy.scipy.pandas和matplotlib.要利用Python进行科学计算,就需要一一安装所需的模块,而这些模块可能又依赖于其它的软件包或库,因而安装和使用起来相对麻烦.幸好有人专门在做这一类事情,将科学计算所需要的模块都编译好,然后打包以发行版的形式供用户使用,Anaconda就是其中一个常用的科学计算发行版. 我们从网站(

windows下如何快速优雅的使用python的科学计算库?

Python是一种强大的编程语言,其提供了很多用于科学计算的模块,常见的包括numpy.scipy.pandas和matplotlib.要利用Python进行科学计算,就需要一一安装所需的模块,而这些模块可能又依赖于其它的软件包或库,因而安装和使用起来相对麻烦.幸好有人专门在做这一类事情,将科学计算所需要的模块都编译好,然后打包以发行版的形式供用户使用,Anaconda就是其中一个常用的科学计算发行版. 我们从网站(链接1)下载的默认的Anaconda版本已经内置了很多库(链接2),包括nump

科学计算器(可实现带括号的浮点数四则运算)

/* Name: 科学计算器(可实现带括号的浮点数四则运算) Copyright: Author: Date: 15-09-14 21:49 Description: 从文件读入计算表达式,将计算表达式转换为逆波兰表达式,然后计算出逆波兰表达式的值,最后输出答案. */ #include<stdio.h> #include<stdlib.h> #include<malloc.h> #include<math.h> #define MAXSIZE 200 vo

百度的科学计算器(简单)

今年,百度的科学计算器进行了重大更新,可以计算更为复杂的表达式了. 定义表达式中存在加减运算.括号.函数调用.强制类型转换这几种运算.其中数值的类型有整型与浮点型两种.并且, 整型与整型加减运算的结果为整型: 整型与浮点型加减运算结果为浮点型: 浮点型与浮点型加减运算结果为浮点型. 强制类型转换符 包括int(x)与float(x),其中float(x)运算符可以将数值x的类型强制转为浮点型,int(x)运算符可以将数值x的类型强制转为整型.对于浮点型转整型,采用截尾法,例如:int(1.6)=

结对项目-增强型科学计算器

题目:增强型计算器   1. 题目简介:       项目采用结对编程方式编写,完成一个图形界面的计算器,可以在标准计算器和科学计算器之间切换,标准计算器的基本功能有:加.减.乘.除基本算术运算:科学计算器的基本功能有:三角函数.进制转换.对数.阶乘:在普通科学计算器基础上新增加:求解一元二次方程,求解勾股定理. 2.基本功能与要求: 1).标准计算器:加.减.乘.除.求平方根: 2).科学计算器:进制转换.求解三角函数.对数运算.阶乘: 3).一元二次方程:求一元二次方程的解: 4).勾股定理

Qt版科学计算器

Qt版科学计算器 转载请标明出处:牟尼的专栏 http://blog.csdn.net/u012027907 之前做过<VC版科学计算器>,这也是我学VC++时的第一个大作业,通过科学计算器的开发使用我学到了很多东西,也让我逐渐喜欢上了编程.最近在学习Qt,所以将当时在VC下写过的一些东西在Qt下重写了一遍,其实主要还是与显示等有关的东西需要重写,要使用Qt的显示方式,而其他的核心的算法等都还是使用VC下C++的源码. 下面是Qt版的运行截图: 标准版: 科学版: 头文件中变量和槽的声明: c

收集的Java科学计算库

将数学.物理.生物.航天.经济学等的计算集成到单一系统架构,提供了细致全面的计算系统. 科学计算框架 Catalano http://www.oschina.net/p/catalano Catalano Framework 是一个 Java 和 Android 的科学计算框架. 主要计算功能: 图像处理 模糊逻辑 数学计算 统计 机器学习 神经网络 科学计算可移植扩展工具包 PETSc http://www.oschina.net/p/petsc PETSc(Portable, Extensi