Skip to content

Commit

Permalink
add strict mode
Browse files Browse the repository at this point in the history
  • Loading branch information
wenjunxiao committed Aug 24, 2020
1 parent 2fcfd3c commit aaae38a
Show file tree
Hide file tree
Showing 11 changed files with 300 additions and 33 deletions.
3 changes: 3 additions & 0 deletions .babelrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"presets": ["@babel/preset-env"]
}
51 changes: 45 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ $C(3.146).floor(2) // 3.14
$C(3.151).fv(2) // 3.15
```

### `r(precision)`/`ru(precision)`/`re(precision)`/`rc(precision)`/`rf(precision)`
#### `r(precision)`/`ru(precision)`/`re(precision)`/`rc(precision)`/`rf(precision)`

Use rounding method to round and return itself, usually in the middle of calculations
```js
Expand All @@ -126,27 +126,27 @@ $C(1).div(8).rf(2).mul(5).v() // 0.6
- `rc` use ceil rounding method
- `rf` use floor rounding method

### `format(fmt='##0.00', prefix='', suffix='')`
#### `format(fmt='##0.00', prefix='', suffix='')`
Format the value with special formatter
```js
$C(1234.1).format('##0.00') // 1,234.10
$C(1234.1).format('##0.00', '$') // $1,234.10
$C(12.3).format('##0.00', '', '%') // 12.30%
```

### `v()`
#### `v()`
Return the number value of current result.
```js
$C(1).add(2).v() // number value `3`
```

### `vs()`
#### `vs()`
Return the string value of current result.
```js
$C(1).add(2).vs() // string value `3`
```

### `ve()`
#### `ve()`
Return the scientific notation value of current result.
```js
$C(10).mul(100).ve() // string value `1e+3`
Expand Down Expand Up @@ -204,7 +204,7 @@ $C.$square = ($x)=>$C(Math.pow($x,2))
1 + square(1 + 1) // 5
```

### Expression settings
#### Expression settings

Settings arguments`{[prefix][format][mode][suffix]}`

Expand All @@ -222,3 +222,42 @@ $C.$square = ($x)=>$C(Math.pow($x,2))
- `e` specify the result to return a scientific, `(1.123){e}` means `$C(1.123).ve()`
- `P` specify the result as a percentage with the suffix `%`, `(1.123){.P}` means `$C(1.123).mul(100).format('.','','%')`
* `suffix` used in `format()` as `suffix` argument, supports `%`

#### Special format

* Support front currency, for example
```js
$C.ocompile('$(1.1 + 1.2)') // $2.3
$C.ocompile('¥(1.1 + 1.2)') // ¥2.3
```
* Support function alias, for example`(...).round(2)`
```js
$C.ocompile('(1.1 + 1.2).round(2)') // $C(1.1).add(1.2).r(2).v()
$C.ocompile('(1.1 + 1.2).upRound(2)') // $C(1.1).add(1.2).ru(2).v()
$C.ocompile('(1.1 + 1.2).evenRound(2)') // $C(1.1).add(1.2).re(2).v()
$C.ocompile('(1.1 + 1.2).ceil(2)') // $C(1.1).add(1.2).rc(2).v()
$C.ocompile('(1.1 + 1.2).floor(2)') // $C(1.1).add(1.2).rf(2).v()
```

### `$compile(expr, ...args)`/`$ccompile(expr, ...args)`/`$cc(expr, ...args)`/`$ocompile(expr)`/`$oc(expr)`

Compile the arithmetic expression to corresponding function, which the function's result is `Calculator`. Used to calculate intermediate results, so formatting cannot be used.
```js
const fn = $C.$ocompile('((x + y) * z){.2R}') // $(x).add(y).mul(z).r(2)
const result = fn({x: 1, y: 2, z: 3}) // result is instanceof Calculator
result.format('##0.00') // 9.00
result.format('##0.0', '$') // $9.0
```

### `eval(expr, ...args)`

Calculate the value of the expression. First half of the `args` is the name, and the second half is the value.
```js
$C.eval('x + y', 'x', 'y', 1, 2) // 3
$C.eval('1 + 2') // 3
$C.eval('a.x + a.y', 'a', {x:1, y:2}) // 3
```

### `withStrict()/withoutStrict()`

Use or not use strict mode. With strict mode, will check the input value whther is a number by `isNaN`
2 changes: 2 additions & 0 deletions bin/pcalc.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
#!/usr/bin/env node
const C = require('../index')
const { resetDebug } = require('../lib/compile')

resetDebug(process.env.DEBUG_CALCULATOR_COMPILE)
const pos = process.argv.indexOf('--listen')
if (pos > 0) {
const http = require('http')
Expand Down
2 changes: 1 addition & 1 deletion dist/calculator.min.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion dist/calculator.min.js.map

Large diffs are not rendered by default.

109 changes: 108 additions & 1 deletion lib/calculator.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ const _pow = Math.pow
const _ceil = Math.ceil
const _floor = Math.floor
const _round = Math.round
const _abs = Math.abs

/**
* 计算器
Expand Down Expand Up @@ -689,10 +690,112 @@ class Calculator {
[inspect.custom] () {
return this.v()
}

/**
* 是否为0
*/
isZero () {
return this._vi === 0
}

/**
* 正数
*/
positive () {
return this._vi > 0
}

/**
* 负数
*/
negative () {
return this._vi < 0
}

/**
* 取绝对值
*/
abs () {
this._vi = _abs(this._vi)
this._v = null
return this;
}

}

class StrictCalculator extends Calculator {
constructor(v) {
super(v)
if (!(v && v._vi)) {
this.check(v)
}
}

check (v) {
if (isNaN(v)) throw new Error(`Invalid number[${v}]`)
}

$check (v) {
if (typeof v._vi === 'undefined') throw new Error(`Invalid calculator[${v}]`)
}

add (v) {
this.check(v)
return super.add(v)
}

$add (v) {
this.$check(v)
return super.$add(v)
}

sub (v) {
this.check(v)
return super.sub(v)
}

$sub (v) {
this.$check(v)
return super.$sub(v)
}

mul (v) {
this.check(v)
return super.mul(v)
}

$mul (v) {
this.$check(v)
return super.$mul(v)
}

div (v) {
this.check(v)
return super.div(v)
}

$div (v) {
this.$check(v)
return super.$div(v)
}
}

function calculator (v) {
return new Calculator(v)
return new calculator.Calculator(v)
}

function strict (v) {
return new StrictCalculator(v)
}

function withStrict () {
calculator.Calculator = StrictCalculator;
return this;
}

function withoutStrict () {
calculator.Calculator = Calculator;
return this;
}

Calculator.prototype._print = console.error.bind(console, '[debug]')
Expand All @@ -706,8 +809,12 @@ calculator.resetDebug = function () {
}

calculator.Calculator = Calculator
calculator.StrictCalculator = StrictCalculator;
/**
* 初始化一个计算器
*/
calculator.$ = calculator
calculator.strict = strict;
calculator.withStrict = withStrict;
calculator.withoutStrict = withoutStrict;
module.exports = calculator
51 changes: 43 additions & 8 deletions lib/compile.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,6 @@ function resetDebug (enable) {
}
}

resetDebug(process.env.DEBUG_CALCULATOR_COMPILE)

/**
* 处理乘除表达式
* @param {String} expr 表达式
Expand Down Expand Up @@ -105,6 +103,19 @@ function evalArgs (s) {
return s
}

const SPECIAL_ALIAS = {
round: 'R',
r: 'R',
upRound: 'U',
ru: 'U',
evenRound: 'E',
re: 'E',
ceil: 'C',
rc: 'C',
floor: 'F',
rf: 'F'
};

/**
* 将表达式构建成计算器表达式
* @param {String} expr 表达式
Expand All @@ -113,7 +124,12 @@ function build (expr) {
if (/([\)\d]+([\+\-\*\/])\s*[\(\d]+)/.test(expr) || /([\)\d]+\s*([\+\-\*\/])[\(\d]+)/.test(expr)) {
throw new Error(`运算符[${RegExp.$2}]前后必须保留空格:[${RegExp.$1}]`)
}
expr = expr.replace(/^([\$\¥]?)(.*)(%?)$/, '($2){$1.$3}')
// 支持前置货币和特殊函数
expr = expr.replace(/^([\$\¥]?)(.*)(%?)$/, '($2){$1.$3}').replace(/\)\.(\w+)\(\s*(\d*)\s*\)/mg, ($0, $1, $2) => {
const v = SPECIAL_ALIAS[$1];
if (!v) throw new Error('Unsupported format alias: ' + $1);
return `){.${$2}${v}}`;
});
let found = 0
const groups = []
const GROUP_FLAG = GROUP_PREFIX + '__' + Date.now() + '__'
Expand Down Expand Up @@ -219,14 +235,18 @@ function build (expr) {
}

/**
* 编译表达式
* @param {String} expr 表达式
* @param {String[]} names 传入表达式中的参数名称列表
*
* @param {*} final 表达式结果返回最终结果(`Number|String`)还是过程结果(`Calculator`),
* 如果表达式中中已经出现指定模式或者格式化`format`等导致产生最终结果,那么返回
* @param {*} expr
* @param {...any} names
* @returns {Function} 计算函数
*/
function compile (expr, ...names) {
function _compile (final, expr, ...names) {
let exp = build(expr)
if (!/\.(f|format|v|vs|ve)\([^)]*\)$/.test(exp)) {
if (/\.(f|format|v|vs|ve)\([^)]*\)$/.test(exp)) {
if (!final) throw new Error(`The expression is not allowed to contain the final result: ${exp}`);
} else if (final) {
exp += '.v()'
}
try {
Expand All @@ -249,10 +269,25 @@ function compile (expr, ...names) {
}
}

function $compile (expr, ...names) {
return _compile(false, expr, ...names);
}

/**
* 编译表达式
* @param {String} expr 表达式
* @param {String[]} names 传入表达式中的参数名称列表
* @returns {Function} 计算函数
*/
function compile (expr, ...names) {
return _compile(true, expr, ...names);
}

module.exports = {
resetDebug,
evalAS,
evalMD,
build,
$compile,
compile
}

0 comments on commit aaae38a

Please sign in to comment.