summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.github/dependabot.yml10
-rw-r--r--calc.go40
-rw-r--r--calc_test.go73
-rw-r--r--pivotTable.go12
-rw-r--r--pivotTable_test.go12
-rw-r--r--sheet.go10
6 files changed, 114 insertions, 43 deletions
diff --git a/.github/dependabot.yml b/.github/dependabot.yml
new file mode 100644
index 0000000..dfefea1
--- /dev/null
+++ b/.github/dependabot.yml
@@ -0,0 +1,10 @@
+version: 2
+updates:
+- package-ecosystem: github-actions
+ directory: /
+ schedule:
+ interval: monthly
+- package-ecosystem: gomod
+ directory: /
+ schedule:
+ interval: monthly
diff --git a/calc.go b/calc.go
index 8a80fb5..f636d7f 100644
--- a/calc.go
+++ b/calc.go
@@ -829,6 +829,8 @@ func newEmptyFormulaArg() formulaArg {
func (f *File) evalInfixExp(sheet, cell string, tokens []efp.Token) (formulaArg, error) {
var err error
opdStack, optStack, opfStack, opfdStack, opftStack, argsStack := NewStack(), NewStack(), NewStack(), NewStack(), NewStack(), NewStack()
+ var inArray, inArrayRow bool
+ var arrayRow []formulaArg
for i := 0; i < len(tokens); i++ {
token := tokens[i]
@@ -841,6 +843,14 @@ func (f *File) evalInfixExp(sheet, cell string, tokens []efp.Token) (formulaArg,
// function start
if isFunctionStartToken(token) {
+ if token.TValue == "ARRAY" {
+ inArray = true
+ continue
+ }
+ if token.TValue == "ARRAYROW" {
+ inArrayRow = true
+ continue
+ }
opfStack.Push(token)
argsStack.Push(list.New().Init())
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,
if token.TType == efp.TokenTypeOperand && token.TSubType == efp.TokenSubTypeLogical {
argsStack.Peek().(*list.List).PushBack(newStringFormulaArg(token.TValue))
}
-
+ if inArrayRow && isOperand(token) {
+ arrayRow = append(arrayRow, tokenToFormulaArg(token))
+ continue
+ }
+ if inArrayRow && isFunctionStopToken(token) {
+ inArrayRow = false
+ continue
+ }
+ if inArray && isFunctionStopToken(token) {
+ argsStack.Peek().(*list.List).PushBack(opfdStack.Pop())
+ arrayRow, inArray = []formulaArg{}, false
+ continue
+ }
if err = f.evalInfixExpFunc(sheet, cell, token, nextToken, opfStack, opdStack, opftStack, opfdStack, argsStack); err != nil {
return newEmptyFormulaArg(), err
}
@@ -1274,6 +1296,15 @@ func isOperand(token efp.Token) bool {
return token.TType == efp.TokenTypeOperand && (token.TSubType == efp.TokenSubTypeNumber || token.TSubType == efp.TokenSubTypeText)
}
+// tokenToFormulaArg create a formula argument by given token.
+func tokenToFormulaArg(token efp.Token) formulaArg {
+ if token.TSubType == efp.TokenSubTypeNumber {
+ num, _ := strconv.ParseFloat(token.TValue, 64)
+ return newNumberFormulaArg(num)
+ }
+ return newStringFormulaArg(token.TValue)
+}
+
// parseToken parse basic arithmetic operator priority and evaluate based on
// operators and operands.
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
}
// opd
if isOperand(token) {
- if token.TSubType == efp.TokenSubTypeNumber {
- num, _ := strconv.ParseFloat(token.TValue, 64)
- opdStack.Push(newNumberFormulaArg(num))
- } else {
- opdStack.Push(newStringFormulaArg(token.TValue))
- }
+ opdStack.Push(tokenToFormulaArg(token))
}
return nil
}
diff --git a/calc_test.go b/calc_test.go
index 2cbcc97..b71e93b 100644
--- a/calc_test.go
+++ b/calc_test.go
@@ -60,55 +60,88 @@ func TestCalcCellValue(t *testing.T) {
"=1&2": "12",
"=15%": "0.15",
"=1+20%": "1.2",
+ "={1}+2": "3",
+ "=1+{2}": "3",
+ "={1}+{2}": "3",
`="A"="A"`: "TRUE",
`="A"<>"A"`: "FALSE",
// Engineering Functions
// BESSELI
- "=BESSELI(4.5,1)": "15.3892227537359",
- "=BESSELI(32,1)": "5502845511211.25",
+ "=BESSELI(4.5,1)": "15.3892227537359",
+ "=BESSELI(32,1)": "5502845511211.25",
+ "=BESSELI({32},1)": "5502845511211.25",
+ "=BESSELI(32,{1})": "5502845511211.25",
+ "=BESSELI({32},{1})": "5502845511211.25",
// BESSELJ
- "=BESSELJ(1.9,2)": "0.329925727692387",
+ "=BESSELJ(1.9,2)": "0.329925727692387",
+ "=BESSELJ({1.9},2)": "0.329925727692387",
+ "=BESSELJ(1.9,{2})": "0.329925727692387",
+ "=BESSELJ({1.9},{2})": "0.329925727692387",
// BESSELK
- "=BESSELK(0.05,0)": "3.11423403428966",
- "=BESSELK(0.05,1)": "19.9096743272486",
- "=BESSELK(0.05,2)": "799.501207124235",
- "=BESSELK(3,2)": "0.0615104585619118",
+ "=BESSELK(0.05,0)": "3.11423403428966",
+ "=BESSELK(0.05,1)": "19.9096743272486",
+ "=BESSELK(0.05,2)": "799.501207124235",
+ "=BESSELK(3,2)": "0.0615104585619118",
+ "=BESSELK({3},2)": "0.0615104585619118",
+ "=BESSELK(3,{2})": "0.0615104585619118",
+ "=BESSELK({3},{2})": "0.0615104585619118",
// BESSELY
- "=BESSELY(0.05,0)": "-1.97931100684153",
- "=BESSELY(0.05,1)": "-12.789855163794",
- "=BESSELY(0.05,2)": "-509.61489554492",
- "=BESSELY(9,2)": "-0.229082087487741",
+ "=BESSELY(0.05,0)": "-1.97931100684153",
+ "=BESSELY(0.05,1)": "-12.789855163794",
+ "=BESSELY(0.05,2)": "-509.61489554492",
+ "=BESSELY(9,2)": "-0.229082087487741",
+ "=BESSELY({9},2)": "-0.229082087487741",
+ "=BESSELY(9,{2})": "-0.229082087487741",
+ "=BESSELY({9},{2})": "-0.229082087487741",
// BIN2DEC
"=BIN2DEC(\"10\")": "2",
"=BIN2DEC(\"11\")": "3",
"=BIN2DEC(\"0000000010\")": "2",
"=BIN2DEC(\"1111111110\")": "-2",
"=BIN2DEC(\"110\")": "6",
+ "=BIN2DEC({\"110\"})": "6",
// BIN2HEX
"=BIN2HEX(\"10\")": "2",
"=BIN2HEX(\"0000000001\")": "1",
"=BIN2HEX(\"10\",10)": "0000000002",
"=BIN2HEX(\"1111111110\")": "FFFFFFFFFE",
"=BIN2HEX(\"11101\")": "1D",
+ "=BIN2HEX({\"11101\"})": "1D",
// BIN2OCT
"=BIN2OCT(\"101\")": "5",
"=BIN2OCT(\"0000000001\")": "1",
"=BIN2OCT(\"10\",10)": "0000000002",
"=BIN2OCT(\"1111111110\")": "7777777776",
"=BIN2OCT(\"1110\")": "16",
+ "=BIN2OCT({\"1110\"})": "16",
// BITAND
- "=BITAND(13,14)": "12",
+ "=BITAND(13,14)": "12",
+ "=BITAND({13},14)": "12",
+ "=BITAND(13,{14})": "12",
+ "=BITAND({13},{14})": "12",
// BITLSHIFT
- "=BITLSHIFT(5,2)": "20",
- "=BITLSHIFT(3,5)": "96",
+ "=BITLSHIFT(5,2)": "20",
+ "=BITLSHIFT({3},5)": "96",
+ "=BITLSHIFT(3,5)": "96",
+ "=BITLSHIFT(3,{5})": "96",
+ "=BITLSHIFT({3},{5})": "96",
// BITOR
- "=BITOR(9,12)": "13",
+ "=BITOR(9,12)": "13",
+ "=BITOR({9},12)": "13",
+ "=BITOR(9,{12})": "13",
+ "=BITOR({9},{12})": "13",
// BITRSHIFT
- "=BITRSHIFT(20,2)": "5",
- "=BITRSHIFT(52,4)": "3",
+ "=BITRSHIFT(20,2)": "5",
+ "=BITRSHIFT(52,4)": "3",
+ "=BITRSHIFT({52},4)": "3",
+ "=BITRSHIFT(52,{4})": "3",
+ "=BITRSHIFT({52},{4})": "3",
// BITXOR
- "=BITXOR(5,6)": "3",
- "=BITXOR(9,12)": "5",
+ "=BITXOR(5,6)": "3",
+ "=BITXOR(9,12)": "5",
+ "=BITXOR({9},12)": "5",
+ "=BITXOR(9,{12})": "5",
+ "=BITXOR({9},{12})": "5",
// COMPLEX
"=COMPLEX(5,2)": "5+2i",
"=COMPLEX(5,-9)": "5-9i",
@@ -221,6 +254,7 @@ func TestCalcCellValue(t *testing.T) {
"=HEX2OCT(\"8\",10)": "0000000010",
"=HEX2OCT(\"FFFFFFFFF8\")": "7777777770",
"=HEX2OCT(\"1F3\")": "763",
+ "=HEX2OCT({\"1F3\"})": "763",
// IMABS
"=IMABS(\"2j\")": "2",
"=IMABS(\"-1+2i\")": "2.23606797749979",
@@ -773,6 +807,7 @@ func TestCalcCellValue(t *testing.T) {
"=1+SUM(SUM(1,2*3),4)*4/3+5+(4+2)*3": "38.6666666666667",
"=SUM(1+ROW())": "2",
"=SUM((SUM(2))+1)": "3",
+ "=SUM({1,2,3,4,\"\"})": "10",
// SUMIF
`=SUMIF(F1:F5, "")`: "0",
`=SUMIF(A1:A5, "3")`: "3",
diff --git a/pivotTable.go b/pivotTable.go
index 28c8632..de671f7 100644
--- a/pivotTable.go
+++ b/pivotTable.go
@@ -102,12 +102,12 @@ type PivotTableField struct {
// types := []string{"Meat", "Dairy", "Beverages", "Produce"}
// region := []string{"East", "West", "North", "South"}
// f.SetSheetRow("Sheet1", "A1", &[]string{"Month", "Year", "Type", "Sales", "Region"})
-// for i := 0; i < 30; i++ {
-// f.SetCellValue("Sheet1", fmt.Sprintf("A%d", i+2), month[rand.Intn(12)])
-// f.SetCellValue("Sheet1", fmt.Sprintf("B%d", i+2), year[rand.Intn(3)])
-// f.SetCellValue("Sheet1", fmt.Sprintf("C%d", i+2), types[rand.Intn(4)])
-// f.SetCellValue("Sheet1", fmt.Sprintf("D%d", i+2), rand.Intn(5000))
-// f.SetCellValue("Sheet1", fmt.Sprintf("E%d", i+2), region[rand.Intn(4)])
+// for row := 2; row < 32; row++ {
+// f.SetCellValue("Sheet1", fmt.Sprintf("A%d", row), month[rand.Intn(12)])
+// f.SetCellValue("Sheet1", fmt.Sprintf("B%d", row), year[rand.Intn(3)])
+// f.SetCellValue("Sheet1", fmt.Sprintf("C%d", row), types[rand.Intn(4)])
+// f.SetCellValue("Sheet1", fmt.Sprintf("D%d", row), rand.Intn(5000))
+// f.SetCellValue("Sheet1", fmt.Sprintf("E%d", row), region[rand.Intn(4)])
// }
// if err := f.AddPivotTable(&excelize.PivotTableOption{
// DataRange: "Sheet1!$A$1:$E$31",
diff --git a/pivotTable_test.go b/pivotTable_test.go
index 2f95ed4..adba2eb 100644
--- a/pivotTable_test.go
+++ b/pivotTable_test.go
@@ -18,12 +18,12 @@ func TestAddPivotTable(t *testing.T) {
types := []string{"Meat", "Dairy", "Beverages", "Produce"}
region := []string{"East", "West", "North", "South"}
assert.NoError(t, f.SetSheetRow("Sheet1", "A1", &[]string{"Month", "Year", "Type", "Sales", "Region"}))
- for i := 0; i < 30; i++ {
- assert.NoError(t, f.SetCellValue("Sheet1", fmt.Sprintf("A%d", i+2), month[rand.Intn(12)]))
- assert.NoError(t, f.SetCellValue("Sheet1", fmt.Sprintf("B%d", i+2), year[rand.Intn(3)]))
- assert.NoError(t, f.SetCellValue("Sheet1", fmt.Sprintf("C%d", i+2), types[rand.Intn(4)]))
- assert.NoError(t, f.SetCellValue("Sheet1", fmt.Sprintf("D%d", i+2), rand.Intn(5000)))
- assert.NoError(t, f.SetCellValue("Sheet1", fmt.Sprintf("E%d", i+2), region[rand.Intn(4)]))
+ for row := 2; row < 32; row++ {
+ assert.NoError(t, f.SetCellValue("Sheet1", fmt.Sprintf("A%d", row), month[rand.Intn(12)]))
+ assert.NoError(t, f.SetCellValue("Sheet1", fmt.Sprintf("B%d", row), year[rand.Intn(3)]))
+ assert.NoError(t, f.SetCellValue("Sheet1", fmt.Sprintf("C%d", row), types[rand.Intn(4)]))
+ assert.NoError(t, f.SetCellValue("Sheet1", fmt.Sprintf("D%d", row), rand.Intn(5000)))
+ assert.NoError(t, f.SetCellValue("Sheet1", fmt.Sprintf("E%d", row), region[rand.Intn(4)]))
}
assert.NoError(t, f.AddPivotTable(&PivotTableOption{
DataRange: "Sheet1!$A$1:$E$31",
diff --git a/sheet.go b/sheet.go
index 1c17f78..47a2063 100644
--- a/sheet.go
+++ b/sheet.go
@@ -1069,12 +1069,12 @@ func (f *File) SetHeaderFooter(sheet string, settings *FormatHeaderFooter) error
return err
}
-// ProtectSheet provides a function to prevent other users from accidentally
-// or deliberately changing, moving, or deleting data in a worksheet. The
+// ProtectSheet provides a function to prevent other users from accidentally or
+// deliberately changing, moving, or deleting data in a worksheet. The
// optional field AlgorithmName specified hash algorithm, support XOR, MD4,
-// MD5, SHA1, SHA256, SHA384, and SHA512 currently, if no hash algorithm
-// specified, will be using the XOR algorithm as default. For example,
-// protect Sheet1 with protection settings:
+// MD5, SHA-1, SHA2-56, SHA-384, and SHA-512 currently, if no hash algorithm
+// specified, will be using the XOR algorithm as default. For example, protect
+// Sheet1 with protection settings:
//
// err := f.ProtectSheet("Sheet1", &excelize.FormatSheetProtection{
// AlgorithmName: "SHA-512",