Skip to content

Commit 1c167b9

Browse files
committedMay 25, 2022
Improves the calculation engine, docs update, and adds the dependabot
- Initialize array formula support for the formula calculation engine - Update example and unit test of `AddPivotTable` - Update the supported hash algorithm of ProtectSheet
1 parent afb2d27 commit 1c167b9

File tree

6 files changed

+114
-43
lines changed

6 files changed

+114
-43
lines changed
 

‎.github/dependabot.yml

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
version: 2
2+
updates:
3+
- package-ecosystem: github-actions
4+
directory: /
5+
schedule:
6+
interval: monthly
7+
- package-ecosystem: gomod
8+
directory: /
9+
schedule:
10+
interval: monthly

‎calc.go

+33-7
Original file line numberDiff line numberDiff line change
@@ -829,6 +829,8 @@ func newEmptyFormulaArg() formulaArg {
829829
func (f *File) evalInfixExp(sheet, cell string, tokens []efp.Token) (formulaArg, error) {
830830
var err error
831831
opdStack, optStack, opfStack, opfdStack, opftStack, argsStack := NewStack(), NewStack(), NewStack(), NewStack(), NewStack(), NewStack()
832+
var inArray, inArrayRow bool
833+
var arrayRow []formulaArg
832834
for i := 0; i < len(tokens); i++ {
833835
token := tokens[i]
834836

@@ -841,6 +843,14 @@ func (f *File) evalInfixExp(sheet, cell string, tokens []efp.Token) (formulaArg,
841843

842844
// function start
843845
if isFunctionStartToken(token) {
846+
if token.TValue == "ARRAY" {
847+
inArray = true
848+
continue
849+
}
850+
if token.TValue == "ARRAYROW" {
851+
inArrayRow = true
852+
continue
853+
}
844854
opfStack.Push(token)
845855
argsStack.Push(list.New().Init())
846856
opftStack.Push(token) // to know which operators belong to a function use the function as a separator
@@ -922,7 +932,19 @@ func (f *File) evalInfixExp(sheet, cell string, tokens []efp.Token) (formulaArg,
922932
if token.TType == efp.TokenTypeOperand && token.TSubType == efp.TokenSubTypeLogical {
923933
argsStack.Peek().(*list.List).PushBack(newStringFormulaArg(token.TValue))
924934
}
925-
935+
if inArrayRow && isOperand(token) {
936+
arrayRow = append(arrayRow, tokenToFormulaArg(token))
937+
continue
938+
}
939+
if inArrayRow && isFunctionStopToken(token) {
940+
inArrayRow = false
941+
continue
942+
}
943+
if inArray && isFunctionStopToken(token) {
944+
argsStack.Peek().(*list.List).PushBack(opfdStack.Pop())
945+
arrayRow, inArray = []formulaArg{}, false
946+
continue
947+
}
926948
if err = f.evalInfixExpFunc(sheet, cell, token, nextToken, opfStack, opdStack, opftStack, opfdStack, argsStack); err != nil {
927949
return newEmptyFormulaArg(), err
928950
}
@@ -1274,6 +1296,15 @@ func isOperand(token efp.Token) bool {
12741296
return token.TType == efp.TokenTypeOperand && (token.TSubType == efp.TokenSubTypeNumber || token.TSubType == efp.TokenSubTypeText)
12751297
}
12761298

1299+
// tokenToFormulaArg create a formula argument by given token.
1300+
func tokenToFormulaArg(token efp.Token) formulaArg {
1301+
if token.TSubType == efp.TokenSubTypeNumber {
1302+
num, _ := strconv.ParseFloat(token.TValue, 64)
1303+
return newNumberFormulaArg(num)
1304+
}
1305+
return newStringFormulaArg(token.TValue)
1306+
}
1307+
12771308
// parseToken parse basic arithmetic operator priority and evaluate based on
12781309
// operators and operands.
12791310
func (f *File) parseToken(sheet string, token efp.Token, opdStack, optStack *Stack) error {
@@ -1318,12 +1349,7 @@ func (f *File) parseToken(sheet string, token efp.Token, opdStack, optStack *Sta
13181349
}
13191350
// opd
13201351
if isOperand(token) {
1321-
if token.TSubType == efp.TokenSubTypeNumber {
1322-
num, _ := strconv.ParseFloat(token.TValue, 64)
1323-
opdStack.Push(newNumberFormulaArg(num))
1324-
} else {
1325-
opdStack.Push(newStringFormulaArg(token.TValue))
1326-
}
1352+
opdStack.Push(tokenToFormulaArg(token))
13271353
}
13281354
return nil
13291355
}

‎calc_test.go

+54-19
Original file line numberDiff line numberDiff line change
@@ -60,55 +60,88 @@ func TestCalcCellValue(t *testing.T) {
6060
"=1&2": "12",
6161
"=15%": "0.15",
6262
"=1+20%": "1.2",
63+
"={1}+2": "3",
64+
"=1+{2}": "3",
65+
"={1}+{2}": "3",
6366
`="A"="A"`: "TRUE",
6467
`="A"<>"A"`: "FALSE",
6568
// Engineering Functions
6669
// BESSELI
67-
"=BESSELI(4.5,1)": "15.3892227537359",
68-
"=BESSELI(32,1)": "5502845511211.25",
70+
"=BESSELI(4.5,1)": "15.3892227537359",
71+
"=BESSELI(32,1)": "5502845511211.25",
72+
"=BESSELI({32},1)": "5502845511211.25",
73+
"=BESSELI(32,{1})": "5502845511211.25",
74+
"=BESSELI({32},{1})": "5502845511211.25",
6975
// BESSELJ
70-
"=BESSELJ(1.9,2)": "0.329925727692387",
76+
"=BESSELJ(1.9,2)": "0.329925727692387",
77+
"=BESSELJ({1.9},2)": "0.329925727692387",
78+
"=BESSELJ(1.9,{2})": "0.329925727692387",
79+
"=BESSELJ({1.9},{2})": "0.329925727692387",
7180
// BESSELK
72-
"=BESSELK(0.05,0)": "3.11423403428966",
73-
"=BESSELK(0.05,1)": "19.9096743272486",
74-
"=BESSELK(0.05,2)": "799.501207124235",
75-
"=BESSELK(3,2)": "0.0615104585619118",
81+
"=BESSELK(0.05,0)": "3.11423403428966",
82+
"=BESSELK(0.05,1)": "19.9096743272486",
83+
"=BESSELK(0.05,2)": "799.501207124235",
84+
"=BESSELK(3,2)": "0.0615104585619118",
85+
"=BESSELK({3},2)": "0.0615104585619118",
86+
"=BESSELK(3,{2})": "0.0615104585619118",
87+
"=BESSELK({3},{2})": "0.0615104585619118",
7688
// BESSELY
77-
"=BESSELY(0.05,0)": "-1.97931100684153",
78-
"=BESSELY(0.05,1)": "-12.789855163794",
79-
"=BESSELY(0.05,2)": "-509.61489554492",
80-
"=BESSELY(9,2)": "-0.229082087487741",
89+
"=BESSELY(0.05,0)": "-1.97931100684153",
90+
"=BESSELY(0.05,1)": "-12.789855163794",
91+
"=BESSELY(0.05,2)": "-509.61489554492",
92+
"=BESSELY(9,2)": "-0.229082087487741",
93+
"=BESSELY({9},2)": "-0.229082087487741",
94+
"=BESSELY(9,{2})": "-0.229082087487741",
95+
"=BESSELY({9},{2})": "-0.229082087487741",
8196
// BIN2DEC
8297
"=BIN2DEC(\"10\")": "2",
8398
"=BIN2DEC(\"11\")": "3",
8499
"=BIN2DEC(\"0000000010\")": "2",
85100
"=BIN2DEC(\"1111111110\")": "-2",
86101
"=BIN2DEC(\"110\")": "6",
102+
"=BIN2DEC({\"110\"})": "6",
87103
// BIN2HEX
88104
"=BIN2HEX(\"10\")": "2",
89105
"=BIN2HEX(\"0000000001\")": "1",
90106
"=BIN2HEX(\"10\",10)": "0000000002",
91107
"=BIN2HEX(\"1111111110\")": "FFFFFFFFFE",
92108
"=BIN2HEX(\"11101\")": "1D",
109+
"=BIN2HEX({\"11101\"})": "1D",
93110
// BIN2OCT
94111
"=BIN2OCT(\"101\")": "5",
95112
"=BIN2OCT(\"0000000001\")": "1",
96113
"=BIN2OCT(\"10\",10)": "0000000002",
97114
"=BIN2OCT(\"1111111110\")": "7777777776",
98115
"=BIN2OCT(\"1110\")": "16",
116+
"=BIN2OCT({\"1110\"})": "16",
99117
// BITAND
100-
"=BITAND(13,14)": "12",
118+
"=BITAND(13,14)": "12",
119+
"=BITAND({13},14)": "12",
120+
"=BITAND(13,{14})": "12",
121+
"=BITAND({13},{14})": "12",
101122
// BITLSHIFT
102-
"=BITLSHIFT(5,2)": "20",
103-
"=BITLSHIFT(3,5)": "96",
123+
"=BITLSHIFT(5,2)": "20",
124+
"=BITLSHIFT({3},5)": "96",
125+
"=BITLSHIFT(3,5)": "96",
126+
"=BITLSHIFT(3,{5})": "96",
127+
"=BITLSHIFT({3},{5})": "96",
104128
// BITOR
105-
"=BITOR(9,12)": "13",
129+
"=BITOR(9,12)": "13",
130+
"=BITOR({9},12)": "13",
131+
"=BITOR(9,{12})": "13",
132+
"=BITOR({9},{12})": "13",
106133
// BITRSHIFT
107-
"=BITRSHIFT(20,2)": "5",
108-
"=BITRSHIFT(52,4)": "3",
134+
"=BITRSHIFT(20,2)": "5",
135+
"=BITRSHIFT(52,4)": "3",
136+
"=BITRSHIFT({52},4)": "3",
137+
"=BITRSHIFT(52,{4})": "3",
138+
"=BITRSHIFT({52},{4})": "3",
109139
// BITXOR
110-
"=BITXOR(5,6)": "3",
111-
"=BITXOR(9,12)": "5",
140+
"=BITXOR(5,6)": "3",
141+
"=BITXOR(9,12)": "5",
142+
"=BITXOR({9},12)": "5",
143+
"=BITXOR(9,{12})": "5",
144+
"=BITXOR({9},{12})": "5",
112145
// COMPLEX
113146
"=COMPLEX(5,2)": "5+2i",
114147
"=COMPLEX(5,-9)": "5-9i",
@@ -221,6 +254,7 @@ func TestCalcCellValue(t *testing.T) {
221254
"=HEX2OCT(\"8\",10)": "0000000010",
222255
"=HEX2OCT(\"FFFFFFFFF8\")": "7777777770",
223256
"=HEX2OCT(\"1F3\")": "763",
257+
"=HEX2OCT({\"1F3\"})": "763",
224258
// IMABS
225259
"=IMABS(\"2j\")": "2",
226260
"=IMABS(\"-1+2i\")": "2.23606797749979",
@@ -773,6 +807,7 @@ func TestCalcCellValue(t *testing.T) {
773807
"=1+SUM(SUM(1,2*3),4)*4/3+5+(4+2)*3": "38.6666666666667",
774808
"=SUM(1+ROW())": "2",
775809
"=SUM((SUM(2))+1)": "3",
810+
"=SUM({1,2,3,4,\"\"})": "10",
776811
// SUMIF
777812
`=SUMIF(F1:F5, "")`: "0",
778813
`=SUMIF(A1:A5, "3")`: "3",

‎pivotTable.go

+6-6
Original file line numberDiff line numberDiff line change
@@ -102,12 +102,12 @@ type PivotTableField struct {
102102
// types := []string{"Meat", "Dairy", "Beverages", "Produce"}
103103
// region := []string{"East", "West", "North", "South"}
104104
// f.SetSheetRow("Sheet1", "A1", &[]string{"Month", "Year", "Type", "Sales", "Region"})
105-
// for i := 0; i < 30; i++ {
106-
// f.SetCellValue("Sheet1", fmt.Sprintf("A%d", i+2), month[rand.Intn(12)])
107-
// f.SetCellValue("Sheet1", fmt.Sprintf("B%d", i+2), year[rand.Intn(3)])
108-
// f.SetCellValue("Sheet1", fmt.Sprintf("C%d", i+2), types[rand.Intn(4)])
109-
// f.SetCellValue("Sheet1", fmt.Sprintf("D%d", i+2), rand.Intn(5000))
110-
// f.SetCellValue("Sheet1", fmt.Sprintf("E%d", i+2), region[rand.Intn(4)])
105+
// for row := 2; row < 32; row++ {
106+
// f.SetCellValue("Sheet1", fmt.Sprintf("A%d", row), month[rand.Intn(12)])
107+
// f.SetCellValue("Sheet1", fmt.Sprintf("B%d", row), year[rand.Intn(3)])
108+
// f.SetCellValue("Sheet1", fmt.Sprintf("C%d", row), types[rand.Intn(4)])
109+
// f.SetCellValue("Sheet1", fmt.Sprintf("D%d", row), rand.Intn(5000))
110+
// f.SetCellValue("Sheet1", fmt.Sprintf("E%d", row), region[rand.Intn(4)])
111111
// }
112112
// if err := f.AddPivotTable(&excelize.PivotTableOption{
113113
// DataRange: "Sheet1!$A$1:$E$31",

‎pivotTable_test.go

+6-6
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,12 @@ func TestAddPivotTable(t *testing.T) {
1818
types := []string{"Meat", "Dairy", "Beverages", "Produce"}
1919
region := []string{"East", "West", "North", "South"}
2020
assert.NoError(t, f.SetSheetRow("Sheet1", "A1", &[]string{"Month", "Year", "Type", "Sales", "Region"}))
21-
for i := 0; i < 30; i++ {
22-
assert.NoError(t, f.SetCellValue("Sheet1", fmt.Sprintf("A%d", i+2), month[rand.Intn(12)]))
23-
assert.NoError(t, f.SetCellValue("Sheet1", fmt.Sprintf("B%d", i+2), year[rand.Intn(3)]))
24-
assert.NoError(t, f.SetCellValue("Sheet1", fmt.Sprintf("C%d", i+2), types[rand.Intn(4)]))
25-
assert.NoError(t, f.SetCellValue("Sheet1", fmt.Sprintf("D%d", i+2), rand.Intn(5000)))
26-
assert.NoError(t, f.SetCellValue("Sheet1", fmt.Sprintf("E%d", i+2), region[rand.Intn(4)]))
21+
for row := 2; row < 32; row++ {
22+
assert.NoError(t, f.SetCellValue("Sheet1", fmt.Sprintf("A%d", row), month[rand.Intn(12)]))
23+
assert.NoError(t, f.SetCellValue("Sheet1", fmt.Sprintf("B%d", row), year[rand.Intn(3)]))
24+
assert.NoError(t, f.SetCellValue("Sheet1", fmt.Sprintf("C%d", row), types[rand.Intn(4)]))
25+
assert.NoError(t, f.SetCellValue("Sheet1", fmt.Sprintf("D%d", row), rand.Intn(5000)))
26+
assert.NoError(t, f.SetCellValue("Sheet1", fmt.Sprintf("E%d", row), region[rand.Intn(4)]))
2727
}
2828
assert.NoError(t, f.AddPivotTable(&PivotTableOption{
2929
DataRange: "Sheet1!$A$1:$E$31",

‎sheet.go

+5-5
Original file line numberDiff line numberDiff line change
@@ -1069,12 +1069,12 @@ func (f *File) SetHeaderFooter(sheet string, settings *FormatHeaderFooter) error
10691069
return err
10701070
}
10711071

1072-
// ProtectSheet provides a function to prevent other users from accidentally
1073-
// or deliberately changing, moving, or deleting data in a worksheet. The
1072+
// ProtectSheet provides a function to prevent other users from accidentally or
1073+
// deliberately changing, moving, or deleting data in a worksheet. The
10741074
// optional field AlgorithmName specified hash algorithm, support XOR, MD4,
1075-
// MD5, SHA1, SHA256, SHA384, and SHA512 currently, if no hash algorithm
1076-
// specified, will be using the XOR algorithm as default. For example,
1077-
// protect Sheet1 with protection settings:
1075+
// MD5, SHA-1, SHA2-56, SHA-384, and SHA-512 currently, if no hash algorithm
1076+
// specified, will be using the XOR algorithm as default. For example, protect
1077+
// Sheet1 with protection settings:
10781078
//
10791079
// err := f.ProtectSheet("Sheet1", &excelize.FormatSheetProtection{
10801080
// AlgorithmName: "SHA-512",

0 commit comments

Comments
 (0)
Please sign in to comment.