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:
Todd 2017-09-16 12:12:55 -05:00
parent fb43078e87
commit 5a5c16b89a
12 changed files with 2291 additions and 1952 deletions

View File

@ -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)
}

View 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
}

View File

@ -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:

View File

@ -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()

View File

@ -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

View File

@ -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]) };
*|; *|;

View File

@ -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}
}

View File

@ -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) {

View File

@ -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{}
} }

View File

@ -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
} }