JavaScript implements advanced scientific Calculator library

Source: Internet
Author: User
Tags cos sin sin cos

The code is not affixed, mainly to explain the idea.

   BNF definition:   //exprn represents the operator expression for priority >=n   expr: = Expr20   expr100: = value//numeric constant has the highest precedence, and of course can merge expr100 into Expr80, This allows you to write less than one parseExpr100 parse sub   -function expr80  : =  (expr) | expr100//followed by parentheses expression   expr60  : =  sin expr60 | cos Expr60 | ln Expr60 | Log Expr60 | SQRT Expr60 | ... | EXPR80////second is a unary function, a unary take negative temporarily not consider   expr50  : = expr60 x^y Expr50 | expr60//Two The priority of the function is higher than the multiplication operation, lower than the unary function, without parentheses, the two-element function from right to left arithmetic;   expr40  : =  expr50 ' * ' expr50 | expr50 '/' Expr50  | expr50//Next is two yuan multiplication operation      ==> expr50 | expr50 (' * ' Expr40) * | Expr50 ('/' expr40) *   expr20  : = expr20 + expr40 | Expr20-expr40  | EXPR40//Next is two-yuan plus minus operation      ==> Expr40 | Expr40 (' + ' expr40) * | Expr40 ('-' expr40) *       //Note that the subtraction operation does not satisfy the commutative law, the right of the meiosis priority must be at least multiply and divide   //bad problem: Expr40, Expr20 Normal writing will lead to left recursion, need to rewrite

function Advancedcalculator () {   //parsing the original input stream:   this.tokens = [];//infix with parentheses, 3 parsing input units: type string (and), Value of type number, operator of type object/string   this.tokens_scan_index = 0;   This.saved_tokens_scan_index_stack = [];       This.value_buffer = [];} Advancedcalculator.prototype = {  Emitbutton:function (token) {      ...


1, the first is to write BNF, here is the point is, non-terminator corresponding sub-expression has a different priority, low priority covers the high, at the same time:

Under normal circumstances, the lower priority will be nested call priority high, if there is a left recursion (+-*/case), at this time equivalent to the operator is left-associative, may also be a direct right recursion, such as the EXPR50 two-dollar infix expression (or unary function), at this time equivalent to the right combination;

Left recursion before parsing directly into recursive descent, it is necessary to introduce an empty expression, which is generally converted to a right-recursive form.

2, grammar analysis can be simplified: the calculator button can be directly corresponding to a token, it is either an operator, such as +-*/sin cos and so on (parentheses as a special operator), or the number contains the dot number.

The sequence of numbers between the 2 operators can be directly spliced, and then directly with the JS number () can be converted to a double value.

3. Error handling can be simplified: each token is entered, the accumulated token sequence is evaluated once, if an error is encountered, the input has a problem (or incomplete)

4, the lowest priority is EXPR20 plus minus expression, but the syntax recursive parsing is starting from EXPR20, note that the bracket expression expr80, it will be the lowest priority EXPR20 to the highest

5, JS by ES5 Standard, VAR variable is a function scope, not a grammatical scope (no ES6 let), so the declaration of Var in the loop next time the best reset

6, JS can be used as an array [] directly as a stack, and pay attention to its core api:unshift/shift/push/pop, but there is no add/remove/insert such a name;

7. Use the following code to define the token constants:

Advancedcalculator.prototype = {   //complex operator is defined as a separate object:   SQRT:  "SQRT",   sin:  "sin",   COS:  "Cos",   Tan: "Tan",   cot: "cot",   log:  "log",//with 10 for the bottom   ln:  "ln",//with E as the base   POW:  "pow",//x^y   PI:Math.PI,//This is a numeric constant, not an operator

These so-called constants are actually associated property objects on the prototype, but are convenient to use, = = can be compared.

8. Using return and throw to use 2 control flows simultaneously, return False indicates that the current stream position resolves to sub-expression a failed, but can try to re-parse as B, the throw is to be sure that the input error, or expect a token input when the flow is over

9. Returns multiple values: Returns a [] array object directly, result, the first element True/false represents a successful failure, and the second is the value of Result[0]==true

This is actually the custom of Erlang, of course, there are convenient destructing can be used in ES6.

Can you actually consider using ES6 to write code and then translate it into ES5 with automated tools? But my code here is still influenced by Java, like Hasmoretokens/nexttoken or something.


TODO: Here the back end (that is, to remove the parsing of the expression evaluation part) Logical write is relatively simple, the equivalent of directly to the AST for the eval recursive call, you can consider the following 2 kinds of improvements:

(1) Support translation as the final complete JS calculation expression--here, if you encounter a special custom function, you can write an anonymous function (function (A, b) {...}) (x, Y) So, of course, the normal can be directly mapped to the JS math.xxx.

(2) Nevertheless, the above 2 evaluation methods are still the idea of the interpreter, not the idea of the compiler, but consider how to translate the result into a serialized sequence of statements with temporary variables ...
In this case, it's a little bit more complicated. The trouble is how to deal with a two-dollar expression, such as: (1+2)-(3+4), translated into a single prefix/suffix expression does not work: because even if the prefix can be translated to-+12+34, or the suffix of the 12+34+-, but the latter form still can not be serialized to a simple traversal evaluation.

Trying to separate the operand from the operator? For example,(1+2)-(3+4) translates to 2 sequences: operand 1234, and operator ++-.

The problem is the same: how do the intermediate temporary variables generated by the first 2 + operations work?

For The compiler's idea, due to the introduction of intermediate temporary variables, in fact, you need to like scheme of CPS conversion, or LLVM such as the high-level virtual machine instructions, such as alloca allocation of local variables and so on. For the expression evaluation here, there are 2 virtual instructions that need to be introduced in the sequence of operators: Push/pop, which is used to manipulate the temporary variable stack of another runtime when the sequence is evaluated.

No additional instructions need to be considered. The temporary (local) variable corresponds to the stack, and the stack requires only Push/pop control access. Note that the problem of expression evaluation has no complex control flow and no new variable for dynamic heap allocation, which is much simpler than the syntax analysis of a common programming language.



Copyright NOTICE: This article for Bo Master original article, without Bo Master permission not reproduced.

JavaScript implements advanced scientific Calculator library

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.