Skip to content

Commit

Permalink
fix(formula): formula select and negative
Browse files Browse the repository at this point in the history
  • Loading branch information
DR-Univer committed Feb 29, 2024
1 parent 60d94a3 commit 9317a5d
Show file tree
Hide file tree
Showing 14 changed files with 252 additions and 84 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -145,10 +145,20 @@ describe('lexer nodeMaker test', () => {
expect(JSON.stringify(node.serialize())).toStrictEqual('{"token":"R_1","st":-1,"ed":-1,"children":["2","1","-","1","-","1","-","1","-"]}');
});

it('minus test Complex', () => {
it('minus test complex', () => {
const node = lexerTreeBuilder.treeBuilder('= ( 2019-09-09 ) ') as LexerNode;
expect(JSON.stringify(node.serialize())).toStrictEqual('{"token":"R_1","st":-1,"ed":-1,"children":[" 2019","09","-","09 ","-"]}');
});

it('minus ref', () => {
const node = lexerTreeBuilder.treeBuilder('= 1--A1 ') as LexerNode;
expect(JSON.stringify(node.serialize())).toStrictEqual('{"token":"R_1","st":-1,"ed":-1,"children":[" 1","-A1 ","-"]}');
});

it('negative ref', () => {
const node = lexerTreeBuilder.treeBuilder('= ------A1 ') as LexerNode;
expect(JSON.stringify(node.serialize())).toStrictEqual('{"token":"R_1","st":-1,"ed":-1,"children":[" -","-","-","-","-","A1 ","-"]}');
});
});

describe('checkIfAddBracket', () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ import type { BaseValueObject } from '../../value-object/base-value-object';
import { Lexer } from '../lexer';
import type { LexerNode } from '../lexer-node';
import { AstTreeBuilder } from '../parser';
import type { ArrayValueObject } from '../../value-object/array-value-object';
import { Minus } from '../../../functions/meta/minus';
import { createCommandTestBed } from './create-command-test-bed';

describe('Test indirect', () => {
Expand Down Expand Up @@ -79,7 +81,7 @@ describe('Test indirect', () => {
testBed.unitId
);

functionService.registerExecutors(new Sum(FUNCTION_NAMES_MATH.SUM), new Plus(FUNCTION_NAMES_META.PLUS));
functionService.registerExecutors(new Sum(FUNCTION_NAMES_MATH.SUM), new Plus(FUNCTION_NAMES_META.PLUS), new Minus(FUNCTION_NAMES_META.MINUS));
});

describe('normal', () => {
Expand All @@ -102,5 +104,65 @@ describe('Test indirect', () => {

expect((result as BaseValueObject).getValue()).toStrictEqual(ErrorType.NAME);
});

it('Minus Minus one', async () => {
const lexerNode = lexer.treeBuilder('=--1');

const astNode = astTreeBuilder.parse(lexerNode as LexerNode);

const result = interpreter.execute(astNode as BaseAstNode);

expect((result as BaseValueObject).getValue()).toStrictEqual(1);
});

it('Minus Minus Minus one', async () => {
const lexerNode = lexer.treeBuilder('=---1');

const astNode = astTreeBuilder.parse(lexerNode as LexerNode);

const result = interpreter.execute(astNode as BaseAstNode);

expect((result as BaseValueObject).getValue()).toStrictEqual(-1);
});

it('Plus Plus Plus Plus Plus Plus one', async () => {
const lexerNode = lexer.treeBuilder('=++++++1');

const astNode = astTreeBuilder.parse(lexerNode as LexerNode);

const result = interpreter.execute(astNode as BaseAstNode);

expect((result as BaseValueObject).getValue()).toStrictEqual(1);
});

it('Plus Plus Plus ref', async () => {
const lexerNode = lexer.treeBuilder('=+++++++A1');

const astNode = astTreeBuilder.parse(lexerNode as LexerNode);

const result = interpreter.execute(astNode as BaseAstNode);

expect((result as ArrayValueObject).getFirstCell().getValue()).toStrictEqual(1);
});

it('Minus Minus Minus ref', async () => {
const lexerNode = lexer.treeBuilder('=---A1');

const astNode = astTreeBuilder.parse(lexerNode as LexerNode);

const result = interpreter.execute(astNode as BaseAstNode);

expect((result as ArrayValueObject).getFirstCell().getValue()).toStrictEqual(-1);
});

it('Minus Minus Minus Minus sum', async () => {
const lexerNode = lexer.treeBuilder('=----sum(A1:A2)');

const astNode = astTreeBuilder.parse(lexerNode as LexerNode);

const result = interpreter.execute(astNode as BaseAstNode);

expect((result as BaseValueObject).getValue()).toStrictEqual(4);
});
});
});
18 changes: 14 additions & 4 deletions packages/engine-formula/src/engine/analysis/lexer-tree-builder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -213,7 +213,6 @@ export class LexerTreeBuilder extends Disposable {
/**
* Estimate the number of right brackets that need to be automatically added to the end of the formula.
* @param formulaString
* @returns
*/
checkIfAddBracket(formulaString: string) {
let lastBracketCount = 0;
Expand Down Expand Up @@ -312,7 +311,7 @@ export class LexerTreeBuilder extends Disposable {

const preItem = sequenceArray[i - 1];

const { segment, currentString, cur } = item;
const { segment, currentString } = item;

if (currentString === matchToken.DOUBLE_QUOTATION) {
maybeString = true;
Expand All @@ -325,11 +324,11 @@ export class LexerTreeBuilder extends Disposable {

let preSegment = preItem?.segment || '';

const startIndex = i - preSegment.length;
let startIndex = i - preSegment.length;

let endIndex = i - 1;

const deleteEndIndex = i - 1;
let deleteEndIndex = i - 1;

if (i === len - 1 && this._isLastMergeString(currentString)) {
preSegment += currentString;
Expand Down Expand Up @@ -359,6 +358,17 @@ export class LexerTreeBuilder extends Disposable {
deleteEndIndex
);
} else if (new RegExp(REFERENCE_SINGLE_RANGE_REGEX).test(preSegmentNotPrefixToken)) {
/**
* =-A1 Separate the negative sign from the ref string.
*/
if (preSegmentNotPrefixToken.length !== preSegmentTrim.length) {
const minusCount = preSegmentTrim.length - preSegmentNotPrefixToken.length;
deleteEndIndex += minusCount;
startIndex += minusCount;

preSegment = this._replacePrefixString(preSegment);
}

this._pushSequenceNode(
sequenceNodes,
{
Expand Down
20 changes: 4 additions & 16 deletions packages/engine-formula/src/engine/analysis/parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@ import { SuffixNodeFactory } from '../ast-node/suffix-node';
import { UnionNodeFactory } from '../ast-node/union-node';
import { ValueNodeFactory } from '../ast-node/value-node';
import { isChildRunTimeParameter, isFirstChildParameter } from '../utils/function-definition';
import { operatorToken } from '../../basics/token';

Check failure on line 46 in packages/engine-formula/src/engine/analysis/parser.ts

View workflow job for this annotation

GitHub Actions / eslint

'operatorToken' is defined but never used
import { getAstNodeTopParent } from '../utils/ast-node-tool';
import { LexerNode } from './lexer-node';

export class AstTreeBuilder extends Disposable {
Expand Down Expand Up @@ -173,15 +175,6 @@ export class AstTreeBuilder extends Disposable {
return newLambdaNode;
}

private _getTopParent(node: BaseAstNode) {
let parent: Nullable<BaseAstNode> = node;
while (parent?.getParent()) {
parent = parent.getParent();
// console.log(parent);
}
return parent;
}

private _parse(lexerNode: LexerNode, parent: BaseAstNode): Nullable<BaseAstNode> {
const children = lexerNode.getChildren();
const childrenCount = children.length;
Expand Down Expand Up @@ -277,7 +270,7 @@ export class AstTreeBuilder extends Disposable {
return ErrorNode.create(ErrorType.NAME);
}

astNode = this._getTopParent(astNode);
astNode = getAstNodeTopParent(astNode);
if (astNode == null) {
return;
}
Expand All @@ -297,18 +290,13 @@ export class AstTreeBuilder extends Disposable {
case NodeType.OPERATOR: {
const parameterNode1 = calculateStack.pop();
const parameterNode2 = calculateStack.pop();

if (parameterNode2) {
parameterNode2.setParent(astNode);
} else {
// console.log('error4', currentAstNode, lexerNode, children, i);
return ErrorNode.create(ErrorType.ERROR);
}

if (parameterNode1) {
parameterNode1.setParent(astNode);
} else {
// console.log('error5', currentAstNode, lexerNode, children, i);
return ErrorNode.create(ErrorType.ERROR);
}

calculateStack.push(astNode);
Expand Down
11 changes: 10 additions & 1 deletion packages/engine-formula/src/engine/ast-node/ast-root-node.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
* limitations under the License.
*/

import { ErrorType, ErrorValueObject } from '../..';
import { DEFAULT_TOKEN_TYPE_ROOT } from '../../basics/token-type';
import { LexerNode } from '../analysis/lexer-node';
import { BaseAstNode } from './base-ast-node';
Expand All @@ -33,7 +34,15 @@ export class AstRootNode extends BaseAstNode {
// } else {
// node.execute(interpreterCalculateProps);
// }
this.setValue(node.getValue());
if (node == null) {
/**
* fix: https://github.com/dream-num/univer/issues/1415
*/
this.setValue(new ErrorValueObject(ErrorType.VALUE));
} else {
this.setValue(node.getValue());
}

// return Promise.resolve(AstNodePromiseType.SUCCESS);
}
}
Expand Down
24 changes: 2 additions & 22 deletions packages/engine-formula/src/engine/ast-node/function-node.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ import type {
} from '../reference-object/base-reference-object';
import { ArrayValueObject, transformToValueObject, ValueObjectFactory } from '../value-object/array-value-object';
import type { BaseValueObject } from '../value-object/base-value-object';
import { prefixHandler } from '../utils/prefixHandler';
import { BaseAstNode, ErrorNode } from './base-ast-node';
import { BaseAstNodeFactory, DEFAULT_AST_NODE_FACTORY_Z_INDEX } from './base-ast-node-factory';
import { NODE_ORDER_MAP, NodeType } from './node-type';
Expand Down Expand Up @@ -266,29 +267,8 @@ export class FunctionNodeFactory extends BaseAstNodeFactory {
return;
}
const token = param.getToken();
let tokenTrim = token.trim().toUpperCase();
let minusPrefixNode: Nullable<PrefixNode>;
let atPrefixNode: Nullable<PrefixNode>;
const prefix = tokenTrim.slice(0, 2);
let sliceLength = 0;
if (new RegExp(prefixToken.MINUS, 'g').test(prefix)) {
const functionExecutor = this._functionService.getExecutor(FUNCTION_NAMES_META.MINUS);
minusPrefixNode = new PrefixNode(this._injector, prefixToken.MINUS, functionExecutor);
sliceLength++;
}

if (new RegExp(prefixToken.AT, 'g').test(prefix)) {
atPrefixNode = new PrefixNode(this._injector, prefixToken.AT);
if (minusPrefixNode) {
// minusPrefixNode.addChildren(atPrefixNode);
atPrefixNode.setParent(minusPrefixNode);
}
sliceLength++;
}

if (sliceLength > 0) {
tokenTrim = tokenTrim.slice(sliceLength);
}
const { tokenTrim, minusPrefixNode, atPrefixNode } = prefixHandler(token.trim().toUpperCase(), this._functionService, this._injector);

if (this._functionService.hasExecutor(tokenTrim)) {
const functionNode = this.create(tokenTrim);
Expand Down
23 changes: 18 additions & 5 deletions packages/engine-formula/src/engine/ast-node/operator-node.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@ import { FUNCTION_NAMES_TEXT } from '../../functions/text/function-names';
import { IFunctionService } from '../../services/function.service';
import { LexerNode } from '../analysis/lexer-node';
import type { BaseReferenceObject, FunctionVariantType } from '../reference-object/base-reference-object';
import type { BaseValueObject } from '../value-object/base-value-object';
import { type BaseValueObject, ErrorValueObject } from '../value-object/base-value-object';
import { NullValueObject, NumberValueObject } from '../value-object/primitive-object';

Check failure on line 29 in packages/engine-formula/src/engine/ast-node/operator-node.ts

View workflow job for this annotation

GitHub Actions / eslint

'NumberValueObject' is defined but never used
import { BaseAstNode, ErrorNode } from './base-ast-node';
import { BaseAstNodeFactory, DEFAULT_AST_NODE_FACTORY_Z_INDEX } from './base-ast-node-factory';
import { NODE_ORDER_MAP, NodeType } from './node-type';
Expand All @@ -47,10 +48,22 @@ export class OperatorNode extends BaseAstNode {
if (this._functionExecutor.name === FUNCTION_NAMES_META.COMPARE) {
(this._functionExecutor as Compare).setCompareType(this.getToken() as compareToken);
}
let object1 = children[0].getValue();
let object2 = children[1].getValue();
if (object1 == null || object2 == null) {
throw new Error('object1 or object2 is null');
let object1 = children[0]?.getValue();
let object2 = children[1]?.getValue();

const token = this.getToken();

if ((object1 == null || object2 == null) && token !== operatorToken.MINUS && token !== operatorToken.PLUS) {
this.setValue(new ErrorValueObject(ErrorType.VALUE));
return;
}

if (object1 == null) {
object1 = new NullValueObject(0);
}

if (object2 == null) {
object2 = new NullValueObject(0);
}

if (object1.isReferenceObject()) {
Expand Down
Loading

0 comments on commit 9317a5d

Please sign in to comment.