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
|
package formula
|
||||||
|
|
||||||
import "math"
|
import (
|
||||||
|
"fmt"
|
||||||
|
"math"
|
||||||
|
)
|
||||||
|
|
||||||
// BinOpType is the binary operation operator type
|
// BinOpType is the binary operation operator type
|
||||||
//go:generate stringer -type=BinOpType
|
//go:generate stringer -type=BinOpType
|
||||||
@ -46,6 +49,22 @@ func (b BinaryExpr) Eval(ctx Context, ev Evaluator) Result {
|
|||||||
lhs := b.lhs.Eval(ctx, ev)
|
lhs := b.lhs.Eval(ctx, ev)
|
||||||
rhs := b.rhs.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
|
// TODO: check for and add support for binary operators on boolean values
|
||||||
switch b.op {
|
switch b.op {
|
||||||
case BinOpTypePlus:
|
case BinOpTypePlus:
|
||||||
@ -128,3 +147,73 @@ func (b BinaryExpr) Eval(ctx Context, ev Evaluator) Result {
|
|||||||
func (b BinaryExpr) Reference(ctx Context, ev Evaluator) Reference {
|
func (b BinaryExpr) Reference(ctx Context, ev Evaluator) Reference {
|
||||||
return ReferenceInvalid
|
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
|
// Sum returns zero with no arguments
|
||||||
res := MakeNumberResult(0)
|
res := MakeNumberResult(0)
|
||||||
for _, a := range args {
|
for _, a := range args {
|
||||||
|
a = a.AsNumber()
|
||||||
switch a.Type {
|
switch a.Type {
|
||||||
case ResultTypeNumber:
|
case ResultTypeNumber:
|
||||||
res.ValueNumber += a.ValueNumber
|
res.ValueNumber += a.ValueNumber
|
||||||
case ResultTypeList:
|
case ResultTypeList:
|
||||||
subSum := Sum(a.ValueList)
|
subSum := Sum(a.ValueList)
|
||||||
|
// error as sum returns only numbers and errors
|
||||||
|
if subSum.Type != ResultTypeNumber {
|
||||||
|
return subSum
|
||||||
|
}
|
||||||
res.ValueNumber += subSum.ValueNumber
|
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:
|
case ResultTypeString:
|
||||||
// treated as zero by Excel
|
// treated as zero by Excel
|
||||||
case ResultTypeError:
|
case ResultTypeError:
|
||||||
|
@ -14,6 +14,7 @@ type yySymType struct {
|
|||||||
node *node
|
node *node
|
||||||
expr Expression
|
expr Expression
|
||||||
args []Expression
|
args []Expression
|
||||||
|
rows [][]Expression
|
||||||
}
|
}
|
||||||
|
|
||||||
const tokenHorizontalRange = 57346
|
const tokenHorizontalRange = 57346
|
||||||
@ -45,6 +46,7 @@ const tokenNE = 57371
|
|||||||
const tokenColon = 57372
|
const tokenColon = 57372
|
||||||
const tokenComma = 57373
|
const tokenComma = 57373
|
||||||
const tokenAmpersand = 57374
|
const tokenAmpersand = 57374
|
||||||
|
const tokenSemi = 57375
|
||||||
|
|
||||||
var yyToknames = [...]string{
|
var yyToknames = [...]string{
|
||||||
"$end",
|
"$end",
|
||||||
@ -79,6 +81,7 @@ var yyToknames = [...]string{
|
|||||||
"tokenColon",
|
"tokenColon",
|
||||||
"tokenComma",
|
"tokenComma",
|
||||||
"tokenAmpersand",
|
"tokenAmpersand",
|
||||||
|
"tokenSemi",
|
||||||
}
|
}
|
||||||
var yyStatenames = [...]string{}
|
var yyStatenames = [...]string{}
|
||||||
|
|
||||||
@ -94,76 +97,82 @@ var yyExca = [...]int{
|
|||||||
|
|
||||||
const yyPrivate = 57344
|
const yyPrivate = 57344
|
||||||
|
|
||||||
const yyLast = 149
|
const yyLast = 175
|
||||||
|
|
||||||
var yyAct = [...]int{
|
var yyAct = [...]int{
|
||||||
|
|
||||||
3, 26, 27, 28, 62, 36, 28, 38, 39, 41,
|
3, 41, 18, 9, 70, 37, 29, 43, 44, 42,
|
||||||
22, 37, 35, 40, 28, 35, 44, 63, 18, 20,
|
27, 28, 29, 45, 46, 36, 67, 71, 29, 23,
|
||||||
17, 45, 46, 65, 11, 47, 48, 49, 50, 51,
|
49, 36, 47, 51, 65, 40, 52, 53, 54, 55,
|
||||||
52, 53, 54, 55, 56, 57, 58, 64, 59, 42,
|
56, 57, 58, 59, 60, 61, 62, 63, 13, 19,
|
||||||
24, 25, 26, 27, 28, 33, 29, 30, 31, 32,
|
64, 66, 42, 14, 15, 16, 17, 72, 21, 69,
|
||||||
34, 9, 1, 35, 19, 10, 2, 8, 0, 0,
|
25, 26, 27, 28, 29, 34, 30, 31, 32, 33,
|
||||||
0, 0, 61, 0, 66, 60, 24, 25, 26, 27,
|
35, 50, 75, 36, 11, 1, 20, 10, 73, 2,
|
||||||
28, 33, 29, 30, 31, 32, 34, 0, 0, 35,
|
42, 74, 76, 68, 25, 26, 27, 28, 29, 34,
|
||||||
24, 25, 26, 27, 28, 33, 29, 30, 31, 32,
|
30, 31, 32, 33, 35, 8, 0, 36, 25, 26,
|
||||||
34, 0, 0, 35, 24, 25, 26, 27, 28, 0,
|
27, 28, 29, 34, 30, 31, 32, 33, 35, 0,
|
||||||
0, 0, 0, 13, 14, 15, 16, 35, 23, 22,
|
0, 36, 25, 26, 27, 28, 29, 0, 0, 0,
|
||||||
21, 5, 0, 12, 0, 6, 7, 0, 0, 0,
|
0, 14, 15, 16, 17, 36, 24, 23, 22, 5,
|
||||||
4, 13, 14, 15, 16, 0, 23, 22, 21, 0,
|
0, 12, 0, 6, 7, 0, 0, 0, 4, 14,
|
||||||
0, 12, 43, 6, 7, 13, 14, 15, 16, 0,
|
15, 16, 17, 0, 24, 23, 22, 38, 0, 12,
|
||||||
23, 22, 21, 0, 0, 12, 0, 6, 7,
|
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{
|
var yyPact = [...]int{
|
||||||
|
|
||||||
96, -1000, -1000, 61, 128, -13, 128, 128, -1000, -1000,
|
104, -1000, -1000, 69, 136, 150, 136, 136, -1000, -1000,
|
||||||
-1000, -1000, 128, -1000, -1000, -1000, -1000, -21, -3, -1000,
|
-1000, -1000, 136, -1000, -1000, -1000, -1000, -1000, -16, 6,
|
||||||
-1000, 114, -1000, -1000, 128, 128, 128, 128, 128, 128,
|
-1000, -1000, 122, -1000, -1000, 136, 136, 136, 136, 136,
|
||||||
128, 128, 128, 128, 128, 128, 61, 128, -20, -20,
|
136, 136, 136, 136, 136, 136, 136, 69, 36, 136,
|
||||||
47, -3, -1000, -1000, -14, -1000, 61, -20, -20, -17,
|
8, -15, -1000, -11, -11, 55, 6, -1000, -1000, -14,
|
||||||
-17, -1000, 75, 75, 75, 75, 75, 75, -9, 21,
|
-1000, 69, -11, -11, -17, -17, -1000, 83, 83, 83,
|
||||||
-1000, -1000, -1000, 128, -1000, -1000, 61,
|
83, 83, 83, -5, 31, -1000, 36, 36, -1000, -1000,
|
||||||
|
-1000, 136, -1000, -15, -1000, -1000, 69,
|
||||||
}
|
}
|
||||||
var yyPgo = [...]int{
|
var yyPgo = [...]int{
|
||||||
|
|
||||||
0, 0, 57, 56, 55, 20, 54, 52, 51, 24,
|
0, 0, 85, 69, 67, 2, 66, 65, 3, 64,
|
||||||
23, 21, 19, 18, 16,
|
62, 61, 48, 39, 38, 25, 20, 1,
|
||||||
}
|
}
|
||||||
var yyR1 = [...]int{
|
var yyR1 = [...]int{
|
||||||
|
|
||||||
0, 7, 3, 3, 3, 8, 8, 8, 8, 1,
|
0, 7, 3, 3, 3, 8, 8, 8, 8, 1,
|
||||||
1, 1, 2, 2, 2, 2, 4, 4, 4, 13,
|
1, 1, 2, 2, 2, 2, 2, 14, 15, 15,
|
||||||
5, 6, 12, 12, 12, 12, 12, 12, 12, 12,
|
17, 17, 4, 4, 4, 13, 5, 6, 12, 12,
|
||||||
12, 12, 12, 12, 9, 9, 9, 14, 14, 11,
|
12, 12, 12, 12, 12, 12, 12, 12, 12, 12,
|
||||||
10, 10,
|
9, 9, 9, 16, 16, 11, 10, 10,
|
||||||
}
|
}
|
||||||
var yyR2 = [...]int{
|
var yyR2 = [...]int{
|
||||||
|
|
||||||
0, 1, 1, 2, 4, 1, 1, 1, 1, 2,
|
0, 1, 1, 2, 4, 1, 1, 1, 1, 2,
|
||||||
2, 1, 1, 1, 1, 3, 1, 2, 1, 1,
|
2, 1, 1, 1, 1, 3, 1, 3, 1, 3,
|
||||||
1, 3, 3, 3, 3, 3, 3, 3, 3, 3,
|
1, 3, 1, 2, 1, 1, 1, 3, 3, 3,
|
||||||
3, 3, 3, 3, 1, 2, 3, 1, 3, 1,
|
3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
|
||||||
1, 0,
|
1, 2, 3, 1, 3, 1, 1, 0,
|
||||||
}
|
}
|
||||||
var yyChk = [...]int{
|
var yyChk = [...]int{
|
||||||
|
|
||||||
-1000, -7, -3, -1, 24, 15, 19, 20, -2, -8,
|
-1000, -7, -3, -1, 24, 15, 19, 20, -2, -8,
|
||||||
-4, -9, 17, 7, 8, 9, 10, -5, -13, -6,
|
-4, -9, 17, -14, 7, 8, 9, 10, -5, -13,
|
||||||
-12, 14, 13, 12, 19, 20, 21, 22, 23, 25,
|
-6, -12, 14, 13, 12, 19, 20, 21, 22, 23,
|
||||||
26, 27, 28, 24, 29, 32, -1, 24, -1, -1,
|
25, 26, 27, 28, 24, 29, 32, -1, 15, 24,
|
||||||
-1, 30, -5, 18, -14, -11, -1, -1, -1, -1,
|
-15, -17, -8, -1, -1, -1, 30, -5, 18, -16,
|
||||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
-11, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||||
18, -5, 18, 31, 16, -10, -1,
|
-1, -1, -1, -1, -1, 16, 33, 31, 18, -5,
|
||||||
|
18, 31, 16, -17, -8, -10, -1,
|
||||||
}
|
}
|
||||||
var yyDef = [...]int{
|
var yyDef = [...]int{
|
||||||
|
|
||||||
0, -2, 1, 2, 0, 0, 0, 0, 11, 12,
|
0, -2, 1, 2, 0, 0, 0, 0, 11, 12,
|
||||||
13, 14, 0, 5, 6, 7, 8, 16, 0, 18,
|
13, 14, 0, 16, 5, 6, 7, 8, 22, 0,
|
||||||
34, 0, 20, 19, 0, 0, 0, 0, 0, 0,
|
24, 40, 0, 26, 25, 0, 0, 0, 0, 0,
|
||||||
0, 0, 0, 0, 0, 0, 3, 0, 9, 10,
|
0, 0, 0, 0, 0, 0, 0, 3, 0, 0,
|
||||||
0, 0, 17, 35, 0, 37, 39, 22, 23, 24,
|
0, 18, 20, 9, 10, 0, 0, 23, 41, 0,
|
||||||
25, 26, 27, 28, 29, 30, 31, 32, 33, 0,
|
43, 45, 28, 29, 30, 31, 32, 33, 34, 35,
|
||||||
15, 21, 36, 41, 4, 38, 40,
|
36, 37, 38, 39, 0, 17, 0, 0, 15, 27,
|
||||||
|
42, 47, 4, 19, 21, 44, 46,
|
||||||
}
|
}
|
||||||
var yyTok1 = [...]int{
|
var yyTok1 = [...]int{
|
||||||
|
|
||||||
@ -174,7 +183,7 @@ var yyTok2 = [...]int{
|
|||||||
2, 3, 4, 5, 6, 7, 8, 9, 10, 11,
|
2, 3, 4, 5, 6, 7, 8, 9, 10, 11,
|
||||||
12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
|
12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
|
||||||
22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
|
22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
|
||||||
32,
|
32, 33,
|
||||||
}
|
}
|
||||||
var yyTok3 = [...]int{
|
var yyTok3 = [...]int{
|
||||||
0,
|
0,
|
||||||
@ -565,106 +574,131 @@ yydefault:
|
|||||||
yyVAL.expr = yyDollar[2].expr
|
yyVAL.expr = yyDollar[2].expr
|
||||||
}
|
}
|
||||||
case 17:
|
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]
|
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:
|
case 20:
|
||||||
yyDollar = yyS[yypt-1 : yypt+1]
|
yyDollar = yyS[yypt-1 : yypt+1]
|
||||||
{
|
{
|
||||||
yyVAL.expr = NewCellRef(yyDollar[1].node.val)
|
yyVAL.args = append(yyVAL.args, yyDollar[1].expr)
|
||||||
}
|
}
|
||||||
case 21:
|
case 21:
|
||||||
yyDollar = yyS[yypt-3 : yypt+1]
|
yyDollar = yyS[yypt-3 : yypt+1]
|
||||||
{
|
{
|
||||||
yyVAL.expr = NewRange(yyDollar[1].expr, yyDollar[3].expr)
|
yyVAL.args = append(yyDollar[1].args, yyDollar[3].expr)
|
||||||
}
|
|
||||||
case 22:
|
|
||||||
yyDollar = yyS[yypt-3 : yypt+1]
|
|
||||||
{
|
|
||||||
yyVAL.expr = NewBinaryExpr(yyDollar[1].expr, BinOpTypePlus, yyDollar[3].expr)
|
|
||||||
}
|
}
|
||||||
case 23:
|
case 23:
|
||||||
yyDollar = yyS[yypt-3 : yypt+1]
|
yyDollar = yyS[yypt-2 : yypt+1]
|
||||||
{
|
{
|
||||||
yyVAL.expr = NewBinaryExpr(yyDollar[1].expr, BinOpTypeMinus, yyDollar[3].expr)
|
yyVAL.expr = NewPrefixExpr(yyDollar[1].expr, yyDollar[2].expr)
|
||||||
}
|
|
||||||
case 24:
|
|
||||||
yyDollar = yyS[yypt-3 : yypt+1]
|
|
||||||
{
|
|
||||||
yyVAL.expr = NewBinaryExpr(yyDollar[1].expr, BinOpTypeMult, yyDollar[3].expr)
|
|
||||||
}
|
}
|
||||||
case 25:
|
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:
|
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:
|
case 27:
|
||||||
yyDollar = yyS[yypt-3 : yypt+1]
|
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:
|
case 28:
|
||||||
yyDollar = yyS[yypt-3 : yypt+1]
|
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:
|
case 29:
|
||||||
yyDollar = yyS[yypt-3 : yypt+1]
|
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:
|
case 30:
|
||||||
yyDollar = yyS[yypt-3 : yypt+1]
|
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:
|
case 31:
|
||||||
yyDollar = yyS[yypt-3 : yypt+1]
|
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:
|
case 32:
|
||||||
yyDollar = yyS[yypt-3 : yypt+1]
|
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:
|
case 33:
|
||||||
yyDollar = yyS[yypt-3 : yypt+1]
|
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:
|
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:
|
case 36:
|
||||||
yyDollar = yyS[yypt-3 : yypt+1]
|
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:
|
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:
|
case 38:
|
||||||
yyDollar = yyS[yypt-3 : yypt+1]
|
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:
|
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]
|
yyDollar = yyS[yypt-0 : yypt+1]
|
||||||
{
|
{
|
||||||
yyVAL.expr = NewEmptyExpr()
|
yyVAL.expr = NewEmptyExpr()
|
||||||
|
@ -14,12 +14,14 @@ package formula
|
|||||||
node *node
|
node *node
|
||||||
expr Expression
|
expr Expression
|
||||||
args []Expression
|
args []Expression
|
||||||
|
rows [][]Expression
|
||||||
}
|
}
|
||||||
|
|
||||||
%type <expr> formula formula1 initial reference referenceItem refFunctionCall
|
%type <expr> formula formula1 initial reference referenceItem refFunctionCall
|
||||||
%type <expr> start constant functionCall argument argument1
|
%type <expr> start constant functionCall argument argument1
|
||||||
%type <expr> binOp prefix
|
%type <expr> binOp prefix constArray
|
||||||
%type <args> arguments
|
%type <rows> constArrayRows
|
||||||
|
%type <args> arguments constArrayCols
|
||||||
|
|
||||||
%token <expr> tokenHorizontalRange tokenReservedName tokenDDECall
|
%token <expr> tokenHorizontalRange tokenReservedName tokenDDECall
|
||||||
%token <node> tokenBool tokenNumber tokenString tokenError tokenErrorRef tokenSheet tokenCell
|
%token <node> tokenBool tokenNumber tokenString tokenError tokenErrorRef tokenSheet tokenCell
|
||||||
@ -27,7 +29,7 @@ package formula
|
|||||||
|
|
||||||
%token tokenLBrace tokenRBrace tokenLParen tokenRParen
|
%token tokenLBrace tokenRBrace tokenLParen tokenRParen
|
||||||
%token tokenPlus tokenMinus tokenMult tokenDiv tokenExp tokenEQ tokenLT tokenGT tokenLEQ tokenGEQ tokenNE
|
%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 tokenEQ tokenLT tokenGT tokenLEQ tokenGEQ tokenNE
|
||||||
%left tokenPlus tokenMinus
|
%left tokenPlus tokenMinus
|
||||||
@ -61,8 +63,19 @@ formula1:
|
|||||||
| reference
|
| reference
|
||||||
| functionCall
|
| functionCall
|
||||||
| tokenLParen formula tokenRParen { $$ = $2 }
|
| 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:
|
reference:
|
||||||
referenceItem
|
referenceItem
|
||||||
| prefix referenceItem { $$ = NewPrefixExpr($1,$2)}
|
| prefix referenceItem { $$ = NewPrefixExpr($1,$2)}
|
||||||
@ -70,7 +83,7 @@ reference:
|
|||||||
|
|
||||||
prefix: tokenSheet { $$ = NewSheetPrefixExpr($1.val) };
|
prefix: tokenSheet { $$ = NewSheetPrefixExpr($1.val) };
|
||||||
|
|
||||||
referenceItem: tokenCell { $$ = NewCellRef($1.val)} ;
|
referenceItem: tokenCell { $$ = NewCellRef($1.val)} ;
|
||||||
|
|
||||||
refFunctionCall:
|
refFunctionCall:
|
||||||
referenceItem tokenColon referenceItem { $$ = NewRange($1,$3) };
|
referenceItem tokenColon referenceItem { $$ = NewRange($1,$3) };
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -89,6 +89,7 @@ import (
|
|||||||
'<>' { l.emit(tokenNE,data[ts:te]) };
|
'<>' { l.emit(tokenNE,data[ts:te]) };
|
||||||
|
|
||||||
':' { l.emit(tokenColon,data[ts:te]) };
|
':' { l.emit(tokenColon,data[ts:te]) };
|
||||||
|
';' { l.emit(tokenSemi,data[ts:te]) };
|
||||||
',' { l.emit(tokenComma,data[ts:te]) };
|
',' { l.emit(tokenComma,data[ts:te]) };
|
||||||
|
|
||||||
*|;
|
*|;
|
||||||
|
@ -22,6 +22,7 @@ const (
|
|||||||
ResultTypeNumber
|
ResultTypeNumber
|
||||||
ResultTypeString
|
ResultTypeString
|
||||||
ResultTypeList
|
ResultTypeList
|
||||||
|
ResultTypeArray
|
||||||
ResultTypeError
|
ResultTypeError
|
||||||
ResultTypeEmpty
|
ResultTypeEmpty
|
||||||
)
|
)
|
||||||
@ -31,11 +32,12 @@ type Result struct {
|
|||||||
ValueNumber float64
|
ValueNumber float64
|
||||||
ValueString string
|
ValueString string
|
||||||
ValueList []Result
|
ValueList []Result
|
||||||
|
ValueArray [][]Result
|
||||||
ErrorMessage string
|
ErrorMessage string
|
||||||
Type ResultType
|
Type ResultType
|
||||||
}
|
}
|
||||||
|
|
||||||
// Value returns a string version of the formula.
|
// Value returns a string version of the result.
|
||||||
func (r Result) Value() string {
|
func (r Result) Value() string {
|
||||||
switch r.Type {
|
switch r.Type {
|
||||||
case ResultTypeNumber:
|
case ResultTypeNumber:
|
||||||
@ -44,6 +46,11 @@ func (r Result) Value() string {
|
|||||||
return r.ValueString
|
return r.ValueString
|
||||||
case ResultTypeString:
|
case ResultTypeString:
|
||||||
return r.ValueString
|
return r.ValueString
|
||||||
|
case ResultTypeList:
|
||||||
|
if len(r.ValueList) == 0 {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return r.ValueList[0].Value()
|
||||||
default:
|
default:
|
||||||
return "unhandled result value"
|
return "unhandled result value"
|
||||||
}
|
}
|
||||||
@ -138,3 +145,13 @@ func MakeStringResult(s string) Result {
|
|||||||
func MakeEmptyResult() Result {
|
func MakeEmptyResult() Result {
|
||||||
return Result{Type: ResultTypeEmpty}
|
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"
|
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 {
|
func (i ResultType) String() string {
|
||||||
if i >= ResultType(len(_ResultType_index)-1) {
|
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
|
package spreadsheet
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
sml "baliance.com/gooxml/schema/schemas.openxmlformats.org/spreadsheetml"
|
sml "baliance.com/gooxml/schema/schemas.openxmlformats.org/spreadsheetml"
|
||||||
@ -42,7 +41,6 @@ func (s MergedCell) Cell() Cell {
|
|||||||
ref = ref[0:idx]
|
ref = ref[0:idx]
|
||||||
return Sheet{w: s.wb, x: s.ws}.Cell(ref)
|
return Sheet{w: s.wb, x: s.ws}.Cell(ref)
|
||||||
}
|
}
|
||||||
fmt.Println("DIE")
|
|
||||||
// couldn't find it, log an error?
|
// couldn't find it, log an error?
|
||||||
return Cell{}
|
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))
|
rel.TargetAttr = gooxml.RelativeFilename(dt, typ, len(wb.charts))
|
||||||
|
|
||||||
default:
|
default:
|
||||||
fmt.Println("unsupported relationship", target, typ)
|
log.Printf("unsupported relationship", target, typ)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user