Skip to content

Commit

Permalink
fix(formula): check syntax (#1543)
Browse files Browse the repository at this point in the history
  • Loading branch information
DR-Univer authored Mar 14, 2024
1 parent 609f907 commit 15a04ed
Show file tree
Hide file tree
Showing 3 changed files with 76 additions and 10 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import { describe, expect, it } from 'vitest';

import type { LexerNode } from '../lexer-node';
import { LexerTreeBuilder } from '../lexer-tree-builder';
import { ErrorType } from '../../../basics/error-type';

describe('lexer nodeMaker test', () => {
const lexerTreeBuilder = new LexerTreeBuilder();
Expand Down Expand Up @@ -175,11 +176,6 @@ describe('lexer nodeMaker test', () => {
expect(JSON.stringify(node.serialize())).toStrictEqual('{"token":"R_1","st":-1,"ed":-1,"children":["0","2","-","2","+"]}');
});

it('plus token error', () => {
const node = lexerTreeBuilder.treeBuilder('=-(2)+*9') as LexerNode;
expect(JSON.stringify(node.serialize())).toStrictEqual('{"token":"R_1","st":-1,"ed":-1,"children":["0","2","-","9","+"]}');
});

it('ref:ref parser', () => {
const node = lexerTreeBuilder.treeBuilder('=SUM(A6:B6:C6:D7,1,1,2)') as LexerNode;
expect(JSON.stringify(node.serialize())).toStrictEqual('{"token":"R_1","st":-1,"ed":-1,"children":[{"token":"SUM","st":0,"ed":2,"children":[{"token":"P_1","st":0,"ed":2,"children":[{"token":":","st":-1,"ed":-1,"children":[{"token":"P_1","st":-1,"ed":-1,"children":[{"token":"A6","st":-1,"ed":-1,"children":[]}]},{"token":"P_1","st":-1,"ed":-1,"children":[{"token":":","st":-1,"ed":-1,"children":[{"token":"P_1","st":-1,"ed":-1,"children":[{"token":"B6","st":-1,"ed":-1,"children":[]}]},{"token":"P_1","st":-1,"ed":-1,"children":[{"token":":","st":-1,"ed":-1,"children":[{"token":"P_1","st":-1,"ed":-1,"children":[{"token":"C6","st":-1,"ed":-1,"children":[]}]},{"token":"P_1","st":-1,"ed":-1,"children":[{"token":"D7","st":-1,"ed":-1,"children":[]}]}]}]}]}]}]}]},{"token":"P_1","st":12,"ed":14,"children":["1"]},{"token":"P_1","st":14,"ed":16,"children":["1"]},{"token":"P_1","st":16,"ed":18,"children":["2"]}]}]}');
Expand All @@ -191,6 +187,28 @@ describe('lexer nodeMaker test', () => {
});
});

describe('check error', () => {
it('plus token error', () => {
const node = lexerTreeBuilder.treeBuilder('=-(2)+*9') as LexerNode;
expect(node).toStrictEqual(ErrorType.VALUE);
});

it('blank in bracket', () => {
const node = lexerTreeBuilder.treeBuilder('=()+9') as LexerNode;
expect(node).toStrictEqual(ErrorType.VALUE);
});

it('plus null value', () => {
const node = lexerTreeBuilder.treeBuilder('=1+(1*)') as LexerNode;
expect(node).toStrictEqual(ErrorType.VALUE);
});

it('open bracket number', () => {
const node = lexerTreeBuilder.treeBuilder('=(1+3)9') as LexerNode;
expect(node).toStrictEqual(ErrorType.VALUE);
});
});

describe('checkIfAddBracket', () => {
it('blank function bracket', () => {
expect(lexerTreeBuilder.checkIfAddBracket('=sum(')).toStrictEqual(1);
Expand Down
54 changes: 49 additions & 5 deletions packages/engine-formula/src/engine/analysis/lexer-tree-builder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -574,7 +574,11 @@ export class LexerTreeBuilder extends Disposable {
}

if (transformSuffix) {
this._suffixExpressionHandler(this._currentLexerNode);
const isValid = this._suffixExpressionHandler(this._currentLexerNode);

if (!isValid) {
return ErrorType.VALUE;
}

FormulaLexerNodeCache.set(formulaString, this._currentLexerNode);
}
Expand All @@ -585,7 +589,7 @@ export class LexerTreeBuilder extends Disposable {
private _suffixExpressionHandler(lexerNode: LexerNode) {
const children = lexerNode.getChildren();
if (!children) {
return;
return false;
}
const childrenCount = children.length;

Expand All @@ -604,9 +608,9 @@ export class LexerTreeBuilder extends Disposable {
if (char === operatorToken.PLUS && this._deletePlusForPreNode(children[i - 1])) {
continue;
}
// =-(2)+*9
// =-(2)+*9, return error
if (char !== operatorToken.PLUS && char !== operatorToken.MINUS && this._deletePlusForPreNode(children[i - 1])) {
continue;
return false;
}
while (symbolStack.length > 0) {
const lastSymbol = symbolStack[symbolStack.length - 1]?.trim();
Expand All @@ -631,6 +635,16 @@ export class LexerTreeBuilder extends Disposable {
} else if (char === matchToken.OPEN_BRACKET) {
symbolStack.push(node as string);
} else if (char === matchToken.CLOSE_BRACKET) {
// =()+9, return error
if (this._checkOpenBracket(children[i - 1])) {
return false;
}

// =1+(1*)
if (this._checkOperator(children[i - 1])) {
return false;
}

while (symbolStack.length > 0) {
const lastSymbol = symbolStack[symbolStack.length - 1]?.trim();
if (!lastSymbol) {
Expand All @@ -645,6 +659,10 @@ export class LexerTreeBuilder extends Disposable {
baseStack.push(symbolStack.pop()!);
}
} else {
// =(1+3)9, return error
if (this._checkCloseBracket(children[i - 1])) {
return false;
}
baseStack.push(node as string);
}
} else {
Expand All @@ -653,9 +671,35 @@ export class LexerTreeBuilder extends Disposable {
}
}
while (symbolStack.length > 0) {
baseStack.push(symbolStack.pop()!);
const symbol = symbolStack.pop()!;
if (symbol === matchToken.OPEN_BRACKET || symbol === matchToken.CLOSE_BRACKET) {
return false;
}
baseStack.push(symbol);
}
lexerNode.setChildren(baseStack);

return true;
}

private _checkCloseBracket(node: Nullable<string | LexerNode>) {
return node === matchToken.CLOSE_BRACKET;
}

private _checkOpenBracket(node: Nullable<string | LexerNode>) {
return node === matchToken.OPEN_BRACKET;
}

private _checkOperator(node: Nullable<string | LexerNode>) {
if (node == null) {
return false;
}

if (node instanceof LexerNode) {
return false;
}

return OPERATOR_TOKEN_SET.has(node);
}

private _deletePlusForPreNode(preNode: Nullable<string | LexerNode>) {
Expand Down
4 changes: 4 additions & 0 deletions packages/engine-formula/src/engine/ast-node/function-node.ts
Original file line number Diff line number Diff line change
Expand Up @@ -270,6 +270,10 @@ export class FunctionNodeFactory extends BaseAstNodeFactory {

const { tokenTrim, minusPrefixNode, atPrefixNode } = prefixHandler(token.trim().toUpperCase(), this._functionService, this._injector);

if (!Number.isNaN(Number(tokenTrim))) {
return ErrorNode.create(ErrorType.VALUE);
}

if (this._functionService.hasExecutor(tokenTrim)) {
const functionNode = this.create(tokenTrim);
if (atPrefixNode) {
Expand Down

0 comments on commit 15a04ed

Please sign in to comment.