mirror of
https://github.com/unidoc/unioffice.git
synced 2025-05-02 22:17:07 +08:00
formula: add initial support for cell arrays
This is a pretty uncommon feature from what I can tell and is not fully supported. The example for the MDETERM function uses an array though, so this will at least add enough support for the examples to compute correctly.
This commit is contained in:
parent
fb43078e87
commit
5a5c16b89a
@ -7,7 +7,10 @@
|
||||
|
||||
package formula
|
||||
|
||||
import "math"
|
||||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
)
|
||||
|
||||
// BinOpType is the binary operation operator type
|
||||
//go:generate stringer -type=BinOpType
|
||||
@ -46,6 +49,22 @@ func (b BinaryExpr) Eval(ctx Context, ev Evaluator) Result {
|
||||
lhs := b.lhs.Eval(ctx, ev)
|
||||
rhs := b.rhs.Eval(ctx, ev)
|
||||
|
||||
// peel off array/list ops first
|
||||
if lhs.Type == rhs.Type {
|
||||
if lhs.Type == ResultTypeArray {
|
||||
if !sameDim(lhs.ValueArray, rhs.ValueArray) {
|
||||
return MakeErrorResult("lhs/rhs should have same dimensions")
|
||||
}
|
||||
return arrayOp(b.op, lhs.ValueArray, rhs.ValueArray)
|
||||
} else if lhs.Type == ResultTypeList {
|
||||
if len(lhs.ValueList) != len(rhs.ValueList) {
|
||||
return MakeErrorResult("lhs/rhs should have same dimensions")
|
||||
}
|
||||
return listOp(b.op, lhs.ValueList, rhs.ValueList)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// TODO: check for and add support for binary operators on boolean values
|
||||
switch b.op {
|
||||
case BinOpTypePlus:
|
||||
@ -128,3 +147,73 @@ func (b BinaryExpr) Eval(ctx Context, ev Evaluator) Result {
|
||||
func (b BinaryExpr) Reference(ctx Context, ev Evaluator) Reference {
|
||||
return ReferenceInvalid
|
||||
}
|
||||
|
||||
// sameDim returns true if the arrays have the same dimensions.
|
||||
func sameDim(lhs, rhs [][]Result) bool {
|
||||
if len(lhs) != len(rhs) {
|
||||
return false
|
||||
}
|
||||
for i := range lhs {
|
||||
if len(lhs[i]) != len(rhs[i]) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func arrayOp(op BinOpType, lhs, rhs [][]Result) Result {
|
||||
// we can assume the arrays are the same size here
|
||||
res := [][]Result{}
|
||||
for i := range lhs {
|
||||
lst := listOp(op, lhs[i], rhs[i])
|
||||
if lst.Type == ResultTypeError {
|
||||
return lst
|
||||
}
|
||||
res = append(res, lst.ValueList)
|
||||
}
|
||||
return MakeArrayResult(res)
|
||||
}
|
||||
|
||||
func listOp(op BinOpType, lhs, rhs []Result) Result {
|
||||
res := []Result{}
|
||||
// we can assume the arrays are the same size here
|
||||
for i := range lhs {
|
||||
l := lhs[i].AsNumber()
|
||||
r := rhs[i].AsNumber()
|
||||
if l.Type != ResultTypeNumber || r.Type != ResultTypeNumber {
|
||||
return MakeErrorResult("non-nunmeric value in binary operation")
|
||||
}
|
||||
switch op {
|
||||
case BinOpTypePlus:
|
||||
res = append(res, MakeNumberResult(l.ValueNumber+r.ValueNumber))
|
||||
case BinOpTypeMinus:
|
||||
res = append(res, MakeNumberResult(l.ValueNumber-r.ValueNumber))
|
||||
case BinOpTypeMult:
|
||||
res = append(res, MakeNumberResult(l.ValueNumber*r.ValueNumber))
|
||||
case BinOpTypeDiv:
|
||||
if r.ValueNumber == 0 {
|
||||
return MakeErrorResultType(ErrorTypeDivideByZero, "")
|
||||
}
|
||||
res = append(res, MakeNumberResult(l.ValueNumber/r.ValueNumber))
|
||||
case BinOpTypeExp:
|
||||
res = append(res, MakeNumberResult(math.Pow(l.ValueNumber, r.ValueNumber)))
|
||||
case BinOpTypeLT:
|
||||
res = append(res, MakeBoolResult(l.ValueNumber < r.ValueNumber))
|
||||
case BinOpTypeGT:
|
||||
res = append(res, MakeBoolResult(l.ValueNumber > r.ValueNumber))
|
||||
case BinOpTypeEQ:
|
||||
res = append(res, MakeBoolResult(l.ValueNumber == r.ValueNumber))
|
||||
case BinOpTypeLEQ:
|
||||
res = append(res, MakeBoolResult(l.ValueNumber <= r.ValueNumber))
|
||||
case BinOpTypeGEQ:
|
||||
res = append(res, MakeBoolResult(l.ValueNumber >= r.ValueNumber))
|
||||
case BinOpTypeNE:
|
||||
res = append(res, MakeBoolResult(l.ValueNumber != r.ValueNumber))
|
||||
// TODO: support concat here
|
||||
// case BinOpTypeConcat:
|
||||
default:
|
||||
return MakeErrorResult(fmt.Sprintf("unsupported list binary op %s", op))
|
||||
}
|
||||
}
|
||||
return MakeListResult(res)
|
||||
}
|
||||
|
32
spreadsheet/formula/constarrayexpr.go
Normal file
32
spreadsheet/formula/constarrayexpr.go
Normal file
@ -0,0 +1,32 @@
|
||||
// Copyright 2017 Baliance. All rights reserved.
|
||||
//
|
||||
// Use of this source code is governed by the terms of the Affero GNU General
|
||||
// Public License version 3.0 as published by the Free Software Foundation and
|
||||
// appearing in the file LICENSE included in the packaging of this file. A
|
||||
// commercial license can be purchased by contacting sales@baliance.com.
|
||||
|
||||
package formula
|
||||
|
||||
type ConstArrayExpr struct {
|
||||
data [][]Expression
|
||||
}
|
||||
|
||||
func NewConstArrayExpr(data [][]Expression) Expression {
|
||||
return &ConstArrayExpr{data}
|
||||
}
|
||||
|
||||
func (c ConstArrayExpr) Eval(ctx Context, ev Evaluator) Result {
|
||||
res := [][]Result{}
|
||||
for _, row := range c.data {
|
||||
r := []Result{}
|
||||
for _, col := range row {
|
||||
r = append(r, col.Eval(ctx, ev))
|
||||
}
|
||||
res = append(res, r)
|
||||
}
|
||||
return MakeArrayResult(res)
|
||||
}
|
||||
|
||||
func (c ConstArrayExpr) Reference(ctx Context, ev Evaluator) Reference {
|
||||
return ReferenceInvalid
|
||||
}
|
@ -24,12 +24,25 @@ func Sum(args []Result) Result {
|
||||
// Sum returns zero with no arguments
|
||||
res := MakeNumberResult(0)
|
||||
for _, a := range args {
|
||||
a = a.AsNumber()
|
||||
switch a.Type {
|
||||
case ResultTypeNumber:
|
||||
res.ValueNumber += a.ValueNumber
|
||||
case ResultTypeList:
|
||||
subSum := Sum(a.ValueList)
|
||||
// error as sum returns only numbers and errors
|
||||
if subSum.Type != ResultTypeNumber {
|
||||
return subSum
|
||||
}
|
||||
res.ValueNumber += subSum.ValueNumber
|
||||
case ResultTypeArray:
|
||||
for _, row := range a.ValueArray {
|
||||
subSum := Sum(row)
|
||||
if subSum.Type != ResultTypeNumber {
|
||||
return subSum
|
||||
}
|
||||
res.ValueNumber += subSum.ValueNumber
|
||||
}
|
||||
case ResultTypeString:
|
||||
// treated as zero by Excel
|
||||
case ResultTypeError:
|
||||
|
@ -14,6 +14,7 @@ type yySymType struct {
|
||||
node *node
|
||||
expr Expression
|
||||
args []Expression
|
||||
rows [][]Expression
|
||||
}
|
||||
|
||||
const tokenHorizontalRange = 57346
|
||||
@ -45,6 +46,7 @@ const tokenNE = 57371
|
||||
const tokenColon = 57372
|
||||
const tokenComma = 57373
|
||||
const tokenAmpersand = 57374
|
||||
const tokenSemi = 57375
|
||||
|
||||
var yyToknames = [...]string{
|
||||
"$end",
|
||||
@ -79,6 +81,7 @@ var yyToknames = [...]string{
|
||||
"tokenColon",
|
||||
"tokenComma",
|
||||
"tokenAmpersand",
|
||||
"tokenSemi",
|
||||
}
|
||||
var yyStatenames = [...]string{}
|
||||
|
||||
@ -94,76 +97,82 @@ var yyExca = [...]int{
|
||||
|
||||
const yyPrivate = 57344
|
||||
|
||||
const yyLast = 149
|
||||
const yyLast = 175
|
||||
|
||||
var yyAct = [...]int{
|
||||
|
||||
3, 26, 27, 28, 62, 36, 28, 38, 39, 41,
|
||||
22, 37, 35, 40, 28, 35, 44, 63, 18, 20,
|
||||
17, 45, 46, 65, 11, 47, 48, 49, 50, 51,
|
||||
52, 53, 54, 55, 56, 57, 58, 64, 59, 42,
|
||||
24, 25, 26, 27, 28, 33, 29, 30, 31, 32,
|
||||
34, 9, 1, 35, 19, 10, 2, 8, 0, 0,
|
||||
0, 0, 61, 0, 66, 60, 24, 25, 26, 27,
|
||||
28, 33, 29, 30, 31, 32, 34, 0, 0, 35,
|
||||
24, 25, 26, 27, 28, 33, 29, 30, 31, 32,
|
||||
34, 0, 0, 35, 24, 25, 26, 27, 28, 0,
|
||||
0, 0, 0, 13, 14, 15, 16, 35, 23, 22,
|
||||
21, 5, 0, 12, 0, 6, 7, 0, 0, 0,
|
||||
4, 13, 14, 15, 16, 0, 23, 22, 21, 0,
|
||||
0, 12, 43, 6, 7, 13, 14, 15, 16, 0,
|
||||
23, 22, 21, 0, 0, 12, 0, 6, 7,
|
||||
3, 41, 18, 9, 70, 37, 29, 43, 44, 42,
|
||||
27, 28, 29, 45, 46, 36, 67, 71, 29, 23,
|
||||
49, 36, 47, 51, 65, 40, 52, 53, 54, 55,
|
||||
56, 57, 58, 59, 60, 61, 62, 63, 13, 19,
|
||||
64, 66, 42, 14, 15, 16, 17, 72, 21, 69,
|
||||
25, 26, 27, 28, 29, 34, 30, 31, 32, 33,
|
||||
35, 50, 75, 36, 11, 1, 20, 10, 73, 2,
|
||||
42, 74, 76, 68, 25, 26, 27, 28, 29, 34,
|
||||
30, 31, 32, 33, 35, 8, 0, 36, 25, 26,
|
||||
27, 28, 29, 34, 30, 31, 32, 33, 35, 0,
|
||||
0, 36, 25, 26, 27, 28, 29, 0, 0, 0,
|
||||
0, 14, 15, 16, 17, 36, 24, 23, 22, 5,
|
||||
0, 12, 0, 6, 7, 0, 0, 0, 4, 14,
|
||||
15, 16, 17, 0, 24, 23, 22, 38, 0, 12,
|
||||
48, 6, 7, 14, 15, 16, 17, 0, 24, 23,
|
||||
22, 38, 0, 12, 0, 6, 7, 14, 15, 16,
|
||||
17, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 39,
|
||||
}
|
||||
var yyPact = [...]int{
|
||||
|
||||
96, -1000, -1000, 61, 128, -13, 128, 128, -1000, -1000,
|
||||
-1000, -1000, 128, -1000, -1000, -1000, -1000, -21, -3, -1000,
|
||||
-1000, 114, -1000, -1000, 128, 128, 128, 128, 128, 128,
|
||||
128, 128, 128, 128, 128, 128, 61, 128, -20, -20,
|
||||
47, -3, -1000, -1000, -14, -1000, 61, -20, -20, -17,
|
||||
-17, -1000, 75, 75, 75, 75, 75, 75, -9, 21,
|
||||
-1000, -1000, -1000, 128, -1000, -1000, 61,
|
||||
104, -1000, -1000, 69, 136, 150, 136, 136, -1000, -1000,
|
||||
-1000, -1000, 136, -1000, -1000, -1000, -1000, -1000, -16, 6,
|
||||
-1000, -1000, 122, -1000, -1000, 136, 136, 136, 136, 136,
|
||||
136, 136, 136, 136, 136, 136, 136, 69, 36, 136,
|
||||
8, -15, -1000, -11, -11, 55, 6, -1000, -1000, -14,
|
||||
-1000, 69, -11, -11, -17, -17, -1000, 83, 83, 83,
|
||||
83, 83, 83, -5, 31, -1000, 36, 36, -1000, -1000,
|
||||
-1000, 136, -1000, -15, -1000, -1000, 69,
|
||||
}
|
||||
var yyPgo = [...]int{
|
||||
|
||||
0, 0, 57, 56, 55, 20, 54, 52, 51, 24,
|
||||
23, 21, 19, 18, 16,
|
||||
0, 0, 85, 69, 67, 2, 66, 65, 3, 64,
|
||||
62, 61, 48, 39, 38, 25, 20, 1,
|
||||
}
|
||||
var yyR1 = [...]int{
|
||||
|
||||
0, 7, 3, 3, 3, 8, 8, 8, 8, 1,
|
||||
1, 1, 2, 2, 2, 2, 4, 4, 4, 13,
|
||||
5, 6, 12, 12, 12, 12, 12, 12, 12, 12,
|
||||
12, 12, 12, 12, 9, 9, 9, 14, 14, 11,
|
||||
10, 10,
|
||||
1, 1, 2, 2, 2, 2, 2, 14, 15, 15,
|
||||
17, 17, 4, 4, 4, 13, 5, 6, 12, 12,
|
||||
12, 12, 12, 12, 12, 12, 12, 12, 12, 12,
|
||||
9, 9, 9, 16, 16, 11, 10, 10,
|
||||
}
|
||||
var yyR2 = [...]int{
|
||||
|
||||
0, 1, 1, 2, 4, 1, 1, 1, 1, 2,
|
||||
2, 1, 1, 1, 1, 3, 1, 2, 1, 1,
|
||||
1, 3, 3, 3, 3, 3, 3, 3, 3, 3,
|
||||
3, 3, 3, 3, 1, 2, 3, 1, 3, 1,
|
||||
1, 0,
|
||||
2, 1, 1, 1, 1, 3, 1, 3, 1, 3,
|
||||
1, 3, 1, 2, 1, 1, 1, 3, 3, 3,
|
||||
3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
|
||||
1, 2, 3, 1, 3, 1, 1, 0,
|
||||
}
|
||||
var yyChk = [...]int{
|
||||
|
||||
-1000, -7, -3, -1, 24, 15, 19, 20, -2, -8,
|
||||
-4, -9, 17, 7, 8, 9, 10, -5, -13, -6,
|
||||
-12, 14, 13, 12, 19, 20, 21, 22, 23, 25,
|
||||
26, 27, 28, 24, 29, 32, -1, 24, -1, -1,
|
||||
-1, 30, -5, 18, -14, -11, -1, -1, -1, -1,
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
18, -5, 18, 31, 16, -10, -1,
|
||||
-4, -9, 17, -14, 7, 8, 9, 10, -5, -13,
|
||||
-6, -12, 14, 13, 12, 19, 20, 21, 22, 23,
|
||||
25, 26, 27, 28, 24, 29, 32, -1, 15, 24,
|
||||
-15, -17, -8, -1, -1, -1, 30, -5, 18, -16,
|
||||
-11, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
-1, -1, -1, -1, -1, 16, 33, 31, 18, -5,
|
||||
18, 31, 16, -17, -8, -10, -1,
|
||||
}
|
||||
var yyDef = [...]int{
|
||||
|
||||
0, -2, 1, 2, 0, 0, 0, 0, 11, 12,
|
||||
13, 14, 0, 5, 6, 7, 8, 16, 0, 18,
|
||||
34, 0, 20, 19, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 3, 0, 9, 10,
|
||||
0, 0, 17, 35, 0, 37, 39, 22, 23, 24,
|
||||
25, 26, 27, 28, 29, 30, 31, 32, 33, 0,
|
||||
15, 21, 36, 41, 4, 38, 40,
|
||||
13, 14, 0, 16, 5, 6, 7, 8, 22, 0,
|
||||
24, 40, 0, 26, 25, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 3, 0, 0,
|
||||
0, 18, 20, 9, 10, 0, 0, 23, 41, 0,
|
||||
43, 45, 28, 29, 30, 31, 32, 33, 34, 35,
|
||||
36, 37, 38, 39, 0, 17, 0, 0, 15, 27,
|
||||
42, 47, 4, 19, 21, 44, 46,
|
||||
}
|
||||
var yyTok1 = [...]int{
|
||||
|
||||
@ -174,7 +183,7 @@ var yyTok2 = [...]int{
|
||||
2, 3, 4, 5, 6, 7, 8, 9, 10, 11,
|
||||
12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
|
||||
22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
|
||||
32,
|
||||
32, 33,
|
||||
}
|
||||
var yyTok3 = [...]int{
|
||||
0,
|
||||
@ -565,106 +574,131 @@ yydefault:
|
||||
yyVAL.expr = yyDollar[2].expr
|
||||
}
|
||||
case 17:
|
||||
yyDollar = yyS[yypt-2 : yypt+1]
|
||||
yyDollar = yyS[yypt-3 : yypt+1]
|
||||
{
|
||||
yyVAL.expr = NewPrefixExpr(yyDollar[1].expr, yyDollar[2].expr)
|
||||
yyVAL.expr = NewConstArrayExpr(yyDollar[2].rows)
|
||||
}
|
||||
case 19:
|
||||
case 18:
|
||||
yyDollar = yyS[yypt-1 : yypt+1]
|
||||
{
|
||||
yyVAL.expr = NewSheetPrefixExpr(yyDollar[1].node.val)
|
||||
yyVAL.rows = append(yyVAL.rows, yyDollar[1].args)
|
||||
}
|
||||
case 19:
|
||||
yyDollar = yyS[yypt-3 : yypt+1]
|
||||
{
|
||||
yyVAL.rows = append(yyDollar[1].rows, yyDollar[3].args)
|
||||
}
|
||||
case 20:
|
||||
yyDollar = yyS[yypt-1 : yypt+1]
|
||||
{
|
||||
yyVAL.expr = NewCellRef(yyDollar[1].node.val)
|
||||
yyVAL.args = append(yyVAL.args, yyDollar[1].expr)
|
||||
}
|
||||
case 21:
|
||||
yyDollar = yyS[yypt-3 : yypt+1]
|
||||
{
|
||||
yyVAL.expr = NewRange(yyDollar[1].expr, yyDollar[3].expr)
|
||||
}
|
||||
case 22:
|
||||
yyDollar = yyS[yypt-3 : yypt+1]
|
||||
{
|
||||
yyVAL.expr = NewBinaryExpr(yyDollar[1].expr, BinOpTypePlus, yyDollar[3].expr)
|
||||
yyVAL.args = append(yyDollar[1].args, yyDollar[3].expr)
|
||||
}
|
||||
case 23:
|
||||
yyDollar = yyS[yypt-3 : yypt+1]
|
||||
yyDollar = yyS[yypt-2 : yypt+1]
|
||||
{
|
||||
yyVAL.expr = NewBinaryExpr(yyDollar[1].expr, BinOpTypeMinus, yyDollar[3].expr)
|
||||
}
|
||||
case 24:
|
||||
yyDollar = yyS[yypt-3 : yypt+1]
|
||||
{
|
||||
yyVAL.expr = NewBinaryExpr(yyDollar[1].expr, BinOpTypeMult, yyDollar[3].expr)
|
||||
yyVAL.expr = NewPrefixExpr(yyDollar[1].expr, yyDollar[2].expr)
|
||||
}
|
||||
case 25:
|
||||
yyDollar = yyS[yypt-3 : yypt+1]
|
||||
yyDollar = yyS[yypt-1 : yypt+1]
|
||||
{
|
||||
yyVAL.expr = NewBinaryExpr(yyDollar[1].expr, BinOpTypeDiv, yyDollar[3].expr)
|
||||
yyVAL.expr = NewSheetPrefixExpr(yyDollar[1].node.val)
|
||||
}
|
||||
case 26:
|
||||
yyDollar = yyS[yypt-3 : yypt+1]
|
||||
yyDollar = yyS[yypt-1 : yypt+1]
|
||||
{
|
||||
yyVAL.expr = NewBinaryExpr(yyDollar[1].expr, BinOpTypeExp, yyDollar[3].expr)
|
||||
yyVAL.expr = NewCellRef(yyDollar[1].node.val)
|
||||
}
|
||||
case 27:
|
||||
yyDollar = yyS[yypt-3 : yypt+1]
|
||||
{
|
||||
yyVAL.expr = NewBinaryExpr(yyDollar[1].expr, BinOpTypeLT, yyDollar[3].expr)
|
||||
yyVAL.expr = NewRange(yyDollar[1].expr, yyDollar[3].expr)
|
||||
}
|
||||
case 28:
|
||||
yyDollar = yyS[yypt-3 : yypt+1]
|
||||
{
|
||||
yyVAL.expr = NewBinaryExpr(yyDollar[1].expr, BinOpTypeGT, yyDollar[3].expr)
|
||||
yyVAL.expr = NewBinaryExpr(yyDollar[1].expr, BinOpTypePlus, yyDollar[3].expr)
|
||||
}
|
||||
case 29:
|
||||
yyDollar = yyS[yypt-3 : yypt+1]
|
||||
{
|
||||
yyVAL.expr = NewBinaryExpr(yyDollar[1].expr, BinOpTypeLEQ, yyDollar[3].expr)
|
||||
yyVAL.expr = NewBinaryExpr(yyDollar[1].expr, BinOpTypeMinus, yyDollar[3].expr)
|
||||
}
|
||||
case 30:
|
||||
yyDollar = yyS[yypt-3 : yypt+1]
|
||||
{
|
||||
yyVAL.expr = NewBinaryExpr(yyDollar[1].expr, BinOpTypeGEQ, yyDollar[3].expr)
|
||||
yyVAL.expr = NewBinaryExpr(yyDollar[1].expr, BinOpTypeMult, yyDollar[3].expr)
|
||||
}
|
||||
case 31:
|
||||
yyDollar = yyS[yypt-3 : yypt+1]
|
||||
{
|
||||
yyVAL.expr = NewBinaryExpr(yyDollar[1].expr, BinOpTypeEQ, yyDollar[3].expr)
|
||||
yyVAL.expr = NewBinaryExpr(yyDollar[1].expr, BinOpTypeDiv, yyDollar[3].expr)
|
||||
}
|
||||
case 32:
|
||||
yyDollar = yyS[yypt-3 : yypt+1]
|
||||
{
|
||||
yyVAL.expr = NewBinaryExpr(yyDollar[1].expr, BinOpTypeNE, yyDollar[3].expr)
|
||||
yyVAL.expr = NewBinaryExpr(yyDollar[1].expr, BinOpTypeExp, yyDollar[3].expr)
|
||||
}
|
||||
case 33:
|
||||
yyDollar = yyS[yypt-3 : yypt+1]
|
||||
{
|
||||
yyVAL.expr = NewBinaryExpr(yyDollar[1].expr, BinOpTypeConcat, yyDollar[3].expr)
|
||||
yyVAL.expr = NewBinaryExpr(yyDollar[1].expr, BinOpTypeLT, yyDollar[3].expr)
|
||||
}
|
||||
case 34:
|
||||
yyDollar = yyS[yypt-3 : yypt+1]
|
||||
{
|
||||
yyVAL.expr = NewBinaryExpr(yyDollar[1].expr, BinOpTypeGT, yyDollar[3].expr)
|
||||
}
|
||||
case 35:
|
||||
yyDollar = yyS[yypt-2 : yypt+1]
|
||||
yyDollar = yyS[yypt-3 : yypt+1]
|
||||
{
|
||||
yyVAL.expr = NewFunction(yyDollar[1].node.val, nil)
|
||||
yyVAL.expr = NewBinaryExpr(yyDollar[1].expr, BinOpTypeLEQ, yyDollar[3].expr)
|
||||
}
|
||||
case 36:
|
||||
yyDollar = yyS[yypt-3 : yypt+1]
|
||||
{
|
||||
yyVAL.expr = NewFunction(yyDollar[1].node.val, yyDollar[2].args)
|
||||
yyVAL.expr = NewBinaryExpr(yyDollar[1].expr, BinOpTypeGEQ, yyDollar[3].expr)
|
||||
}
|
||||
case 37:
|
||||
yyDollar = yyS[yypt-1 : yypt+1]
|
||||
yyDollar = yyS[yypt-3 : yypt+1]
|
||||
{
|
||||
yyVAL.args = append(yyVAL.args, yyDollar[1].expr)
|
||||
yyVAL.expr = NewBinaryExpr(yyDollar[1].expr, BinOpTypeEQ, yyDollar[3].expr)
|
||||
}
|
||||
case 38:
|
||||
yyDollar = yyS[yypt-3 : yypt+1]
|
||||
{
|
||||
yyVAL.args = append(yyDollar[1].args, yyDollar[3].expr)
|
||||
yyVAL.expr = NewBinaryExpr(yyDollar[1].expr, BinOpTypeNE, yyDollar[3].expr)
|
||||
}
|
||||
case 39:
|
||||
yyDollar = yyS[yypt-3 : yypt+1]
|
||||
{
|
||||
yyVAL.expr = NewBinaryExpr(yyDollar[1].expr, BinOpTypeConcat, yyDollar[3].expr)
|
||||
}
|
||||
case 41:
|
||||
yyDollar = yyS[yypt-2 : yypt+1]
|
||||
{
|
||||
yyVAL.expr = NewFunction(yyDollar[1].node.val, nil)
|
||||
}
|
||||
case 42:
|
||||
yyDollar = yyS[yypt-3 : yypt+1]
|
||||
{
|
||||
yyVAL.expr = NewFunction(yyDollar[1].node.val, yyDollar[2].args)
|
||||
}
|
||||
case 43:
|
||||
yyDollar = yyS[yypt-1 : yypt+1]
|
||||
{
|
||||
yyVAL.args = append(yyVAL.args, yyDollar[1].expr)
|
||||
}
|
||||
case 44:
|
||||
yyDollar = yyS[yypt-3 : yypt+1]
|
||||
{
|
||||
yyVAL.args = append(yyDollar[1].args, yyDollar[3].expr)
|
||||
}
|
||||
case 47:
|
||||
yyDollar = yyS[yypt-0 : yypt+1]
|
||||
{
|
||||
yyVAL.expr = NewEmptyExpr()
|
||||
|
@ -14,12 +14,14 @@ package formula
|
||||
node *node
|
||||
expr Expression
|
||||
args []Expression
|
||||
rows [][]Expression
|
||||
}
|
||||
|
||||
%type <expr> formula formula1 initial reference referenceItem refFunctionCall
|
||||
%type <expr> start constant functionCall argument argument1
|
||||
%type <expr> binOp prefix
|
||||
%type <args> arguments
|
||||
%type <expr> binOp prefix constArray
|
||||
%type <rows> constArrayRows
|
||||
%type <args> arguments constArrayCols
|
||||
|
||||
%token <expr> tokenHorizontalRange tokenReservedName tokenDDECall
|
||||
%token <node> tokenBool tokenNumber tokenString tokenError tokenErrorRef tokenSheet tokenCell
|
||||
@ -27,7 +29,7 @@ package formula
|
||||
|
||||
%token tokenLBrace tokenRBrace tokenLParen tokenRParen
|
||||
%token tokenPlus tokenMinus tokenMult tokenDiv tokenExp tokenEQ tokenLT tokenGT tokenLEQ tokenGEQ tokenNE
|
||||
%token tokenColon tokenComma tokenAmpersand
|
||||
%token tokenColon tokenComma tokenAmpersand tokenSemi
|
||||
|
||||
%left tokenEQ tokenLT tokenGT tokenLEQ tokenGEQ tokenNE
|
||||
%left tokenPlus tokenMinus
|
||||
@ -61,8 +63,19 @@ formula1:
|
||||
| reference
|
||||
| functionCall
|
||||
| tokenLParen formula tokenRParen { $$ = $2 }
|
||||
| constArray
|
||||
;
|
||||
|
||||
constArray: tokenLBrace constArrayRows tokenRBrace { $$ = NewConstArrayExpr($2)} ;
|
||||
constArrayRows:
|
||||
constArrayCols { $$ = append($$, $1) }
|
||||
| constArrayRows tokenSemi constArrayCols { $$ = append($1, $3)};
|
||||
|
||||
constArrayCols:
|
||||
constant { $$ = append($$,$1) }
|
||||
| constArrayCols tokenComma constant { $$ = append($1,$3)}
|
||||
|
||||
|
||||
reference:
|
||||
referenceItem
|
||||
| prefix referenceItem { $$ = NewPrefixExpr($1,$2)}
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -89,6 +89,7 @@ import (
|
||||
'<>' { l.emit(tokenNE,data[ts:te]) };
|
||||
|
||||
':' { l.emit(tokenColon,data[ts:te]) };
|
||||
';' { l.emit(tokenSemi,data[ts:te]) };
|
||||
',' { l.emit(tokenComma,data[ts:te]) };
|
||||
|
||||
*|;
|
||||
|
@ -22,6 +22,7 @@ const (
|
||||
ResultTypeNumber
|
||||
ResultTypeString
|
||||
ResultTypeList
|
||||
ResultTypeArray
|
||||
ResultTypeError
|
||||
ResultTypeEmpty
|
||||
)
|
||||
@ -31,11 +32,12 @@ type Result struct {
|
||||
ValueNumber float64
|
||||
ValueString string
|
||||
ValueList []Result
|
||||
ValueArray [][]Result
|
||||
ErrorMessage string
|
||||
Type ResultType
|
||||
}
|
||||
|
||||
// Value returns a string version of the formula.
|
||||
// Value returns a string version of the result.
|
||||
func (r Result) Value() string {
|
||||
switch r.Type {
|
||||
case ResultTypeNumber:
|
||||
@ -44,6 +46,11 @@ func (r Result) Value() string {
|
||||
return r.ValueString
|
||||
case ResultTypeString:
|
||||
return r.ValueString
|
||||
case ResultTypeList:
|
||||
if len(r.ValueList) == 0 {
|
||||
return ""
|
||||
}
|
||||
return r.ValueList[0].Value()
|
||||
default:
|
||||
return "unhandled result value"
|
||||
}
|
||||
@ -138,3 +145,13 @@ func MakeStringResult(s string) Result {
|
||||
func MakeEmptyResult() Result {
|
||||
return Result{Type: ResultTypeEmpty}
|
||||
}
|
||||
|
||||
// MakeArrayResult constructs an array result (matrix).
|
||||
func MakeArrayResult(arr [][]Result) Result {
|
||||
return Result{Type: ResultTypeArray, ValueArray: arr}
|
||||
}
|
||||
|
||||
// MakeListResult constructs a list result.
|
||||
func MakeListResult(list []Result) Result {
|
||||
return Result{Type: ResultTypeList, ValueList: list}
|
||||
}
|
||||
|
@ -4,9 +4,9 @@ package formula
|
||||
|
||||
import "fmt"
|
||||
|
||||
const _ResultType_name = "ResultTypeUnknownResultTypeNumberResultTypeStringResultTypeListResultTypeErrorResultTypeEmpty"
|
||||
const _ResultType_name = "ResultTypeUnknownResultTypeNumberResultTypeStringResultTypeListResultTypeArrayResultTypeErrorResultTypeEmpty"
|
||||
|
||||
var _ResultType_index = [...]uint8{0, 17, 33, 49, 63, 78, 93}
|
||||
var _ResultType_index = [...]uint8{0, 17, 33, 49, 63, 78, 93, 108}
|
||||
|
||||
func (i ResultType) String() string {
|
||||
if i >= ResultType(len(_ResultType_index)-1) {
|
||||
|
BIN
spreadsheet/formula/testdata/formulareference.xlsx
vendored
BIN
spreadsheet/formula/testdata/formulareference.xlsx
vendored
Binary file not shown.
@ -8,7 +8,6 @@
|
||||
package spreadsheet
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
sml "baliance.com/gooxml/schema/schemas.openxmlformats.org/spreadsheetml"
|
||||
@ -42,7 +41,6 @@ func (s MergedCell) Cell() Cell {
|
||||
ref = ref[0:idx]
|
||||
return Sheet{w: s.wb, x: s.ws}.Cell(ref)
|
||||
}
|
||||
fmt.Println("DIE")
|
||||
// couldn't find it, log an error?
|
||||
return Cell{}
|
||||
}
|
||||
|
@ -390,7 +390,7 @@ func (wb *Workbook) onNewRelationship(decMap *zippkg.DecodeMap, target, typ stri
|
||||
rel.TargetAttr = gooxml.RelativeFilename(dt, typ, len(wb.charts))
|
||||
|
||||
default:
|
||||
fmt.Println("unsupported relationship", target, typ)
|
||||
log.Printf("unsupported relationship", target, typ)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user