Skip to content

Commit

Permalink
⚠️feat(token & parser): 支持options
Browse files Browse the repository at this point in the history
BREAKING CHANGE:
parser, token-stream, options.test.js

ISSUES CLOSED:  none
  • Loading branch information
子文 committed May 22, 2020
1 parent 3594a70 commit 3a31864
Show file tree
Hide file tree
Showing 7 changed files with 85 additions and 22 deletions.
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

零依赖,借鉴了 `expr-eval` 底层实现,重构为更适合表达式运算的 `@ali/ceval`;

``` ts
┌───────────────────────────────┐
│ │
Destination: lib/index.js
Expand All @@ -10,6 +11,7 @@
Gzipped Size: 6.35 KB
│ │
└───────────────────────────────┘
```

## usage

Expand Down
18 changes: 6 additions & 12 deletions entry/demo/index.tsx
Original file line number Diff line number Diff line change
@@ -1,20 +1,14 @@
import React from 'react'
import Parser from '@ali/ceval';
// import Parser from '../../src/index';
// import Parser from '@ali/ceval';
import Parser from '../../src/index';

export default () => {
const parser = new Parser();
const parser = new Parser({
endableBitNumber: false
});
// const result = parser.parseString(`0b01`)
// console.log('result: ', result);
console.log(parser.parseString(`
function abs(a,b,c) {
var a = 5;
let b = 1; /* warn */
c = 2;
const d = 4; /* dont't warn */
return(a+b+c);
};
abs(3,4,8)`, {a:1, b:{c: (a,b,c) => { return a+b+c }}}));
console.log(parser.parseString(`c[1]`, {a:1,c: [1,2,3], b:{c: (a,b,c) => { return a+b+c }}}));
console.log(parser.getCurrentValues())

return <div>123</div>
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@ali/ceval",
"version": "0.1.4",
"version": "0.1.5",
"description": "calculation expression",
"main": "./lib/index.js",
"types": "./lib/index.d.ts",
Expand Down
7 changes: 5 additions & 2 deletions src/parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ export default class Parser {
*/
savedNextToken: TypeToken | null = null;

constructor(public parser: Ceval, public tokens: TypeTokenStream, exprInstr: TypeInstruction[]) {
constructor(public ceval: Ceval, public tokens: TypeTokenStream, exprInstr: TypeInstruction[]) {
this.next();

this.inspectParseEnd(exprInstr)
Expand Down Expand Up @@ -384,6 +384,9 @@ export default class Parser {
while (
this.accept(TOKEN_OPERATOR, '.') ||
(contains<string>([TOKEN_SQUARE, TOKEN_NAME], this.current.type) && this.accept(TOKEN_SQUARE, '['))) {
if (!this.ceval.options.allowMemberAccess) {
throw new Error(`options "allowMemberAccess": You have disabled member access and cannot use syntax such as "a.b" "a['b']"`)
}
if (this.current.value === '.') {
this.expect(TOKEN_NAME); // a.name ✔️ a.1×
exprInstr.push(new Instruction(INSTR_MEMBER, this.current.value))
Expand All @@ -397,7 +400,7 @@ export default class Parser {


/**
* 解析字面值、字段值 number||string||operator(typeof cos tan)||[1,2,3]|| {a:1,b:{}} || (expression)
* 解析字面值、字段值 number||string||operator(typeof cos tan)||[1,2,3]|| {a:1,b:{}} || (expression) || function abs() {}
*/
parseField = (exprInstr: TypeInstruction[]): void => {
if (this.accept(TOKEN_OPERATOR, isUnaryOpeator)) {
Expand Down
21 changes: 14 additions & 7 deletions src/token-stream.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ export default class TokenStream {
savedCurrent: null | TypeToken = null;

// eslint-disable-next-line
constructor(public parser: TypeCeval, public expression: string) { }
constructor(public ceval: TypeCeval, public expression: string) { }

/**
* @desc 适用语法校验检查
Expand Down Expand Up @@ -225,12 +225,16 @@ export default class TokenStream {
this.parseError('number bitbase parser error', SyntaxError)
return false
}
if (number !== undefined && !this.ceval.options.endableBitNumber) { // 给出准确的warning
throw new Error(`options "endableBitNumber": You have disabled bitbase number parsing, Not allowed ${number}`)
}
} else if (number10bitReg.test(expr)) { // 十进制
// 100 || 100.1 || 0.1 || .100 || .0 ✅
// parseFloat是支持 0100.1 的。
number = execNumberReg(number10bitReg, expr)
bit = number === undefined ? undefined : 10
} else {

return false
}

Expand Down Expand Up @@ -331,7 +335,7 @@ export default class TokenStream {
* @memberof TokenStream
*/
isConst = (): boolean => {
const keys = Object.keys(this.parser.consts)
const keys = Object.keys(this.ceval.consts)

const result = new RegExp(`^(${keys.join('|')})`).exec(this.getSomeCode(Infinity))

Expand Down Expand Up @@ -410,12 +414,15 @@ export default class TokenStream {
result = unaryMapReg.exec(str)
}

if (result && result[1]) {
this.pos += result[1].length
this.current = this.newToken(TOKEN_OPERATOR, result[1])
return true
if (!result) return false

if(this.ceval.options.endableOperators === false) {
throw new Error(`options "endableOperators": You disabled the operator, Therefore, "${result[1]}" it can not be used`)
}
return false

this.pos += result[1].length
this.current = this.newToken(TOKEN_OPERATOR, result[1])
return true
}

/**
Expand Down
1 change: 1 addition & 0 deletions src/utils/regExp.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ export const booleanReg = /^(false|true)/;
export const commentReg = /^\/\*(.*)\*\//;
export const stringReg = /^\'(.*?)\'|^\"(.*?)\"/;
export const stringGreedyReg = /^\'(.*)\'|^\"(.*)\"/;
// export const regExpReg = /^ExecReg\((.*)\)/;

export const number2bitReg = /^(0b[0|1]{1,})$/;
export const number8bitReg = /^(0[0-7]{1,})$/;
Expand Down
56 changes: 56 additions & 0 deletions test/options.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
/* eslint-disable no-undef */
import Parser from '../src/index'


test('options endableBitNumber = false', () => {
const parse = new Parser({
endableBitNumber: false
}).parseString

expect(() => parse('0b0102')).toThrow(Error)
expect(() => parse('0b0102.1')).toThrow(Error)

expect(() => parse('01.1')).toThrow(Error)
expect(() => parse('027.1')).toThrow(Error)

expect(() => parse('0x01')).toThrow(Error)
expect(() => parse('0xa')).toThrow(Error)
}, 0)

test('options endableOperators = false', () => {
const parse = new Parser({
endableOperators: false
}).parseString

expect(() => parse('1 - 1')).toThrow(Error)
expect(() => parse('1 + 1')).toThrow(Error)
expect(() => parse('1 * 1')).toThrow(Error)
expect(() => parse('1 / 1')).toThrow(Error)
expect(() => parse('1 % 1')).toThrow(Error)
expect(() => parse('1 ^ 1')).toThrow(Error)
expect(() => parse('!undefined')).toThrow(Error)
expect(() => parse('~1')).toThrow(Error)
expect(() => parse('1||2')).toThrow(Error)
expect(() => parse('1&&2')).toThrow(Error)
expect(() => parse('1!=2')).toThrow(Error)
expect(() => parse('1!==2')).toThrow(Error)
expect(() => parse('1==2')).toThrow(Error)
expect(() => parse('1===2')).toThrow(Error)
expect(() => parse('1>=2')).toThrow(Error)
expect(() => parse('1<=2')).toThrow(Error)
expect(() => parse('1>2')).toThrow(Error)
expect(() => parse('1<2')).toThrow(Error)
expect(() => parse(`'a' in ['a','b']`)).toThrow(Error)

}, 0)

test('options allowMemberAccess = false', () => {
const parse = new Parser({
allowMemberAccess: false
}).parseString

expect(() => parse(`a.b`, { a: { b: '1'}})).toThrow(Error)
expect(() => parse(`a["b"]`, { a: { b: '1'}})).toThrow(Error)

expect(() => parse('a[1]', { a: ['1','2','3']})).toThrow(Error)
}, 0)

0 comments on commit 3a31864

Please sign in to comment.