1package formula23import (4 "strings"5 "testing"6)78// bindFuncs adds LET and LAMBDA to the test functions.9func bindFuncs(name string) (Func, bool) {10 switch name {11 case "LET":12 return testFunc{Name: "LET", Min: 3, Max: -1, Step: 2, Binds: BindLet}, true13 case "LAMBDA":14 return testFunc{Name: "LAMBDA", Min: 1, Max: -1, Binds: BindLambda}, true15 }16 return testFuncs(name)17}1819func TestParseArrays(t *testing.T) {20 for _, tt := range []struct{ in, want string }{21 {"={1,2;3,4}", "={1,2;3,4}"},22 {"={ 1 , 2 }*2", "={1,2}*2"},23 {`={"a";"b"}`, `={"a";"b"}`},24 {"={A1:A3,B1:B3}", "={A1:A3,B1:B3}"},25 {"=SUM({1;2},3)", "=SUM({1;2},3)"},26 {"={-1,2+3}", "={-1,2+3}"},27 } {28 n, err := Parse(tt.in, testFuncs)29 if err != nil {30 t.Errorf("Parse(%q): %v", tt.in, err)31 continue32 }33 if got := Text(n); got != tt.want {34 t.Errorf("Text(Parse(%q)) = %q, want %q", tt.in, got, tt.want)35 }36 }37 for _, in := range []string{"={}", "={1,2", "={1 2}", "={1,}"} {38 if _, err := Parse(in, testFuncs); err == nil {39 t.Errorf("Parse(%q) should fail", in)40 }41 }42}4344func TestParseLetAndLambda(t *testing.T) {45 for _, tt := range []struct{ in, want string }{46 {"=LET(x, 1, x+1)", "=LET(x,1,x+1)"},47 {"=LET(Total, A1:A3, SUM(total))", "=LET(Total,A1:A3,SUM(total))"},48 {"=LAMBDA(a, b, a*b)(2, 3)", "=LAMBDA(a,b,a*b)(2,3)"},49 {"=LET(f, LAMBDA(v, v*2), f(4))", "=LET(f,LAMBDA(v,v*2),f(4))"},50 {"=LET(x, 1, x) + x", "=LET(x,1,x)+x"},51 } {52 n, err := Parse(tt.in, bindFuncs)53 if err != nil {54 t.Errorf("Parse(%q): %v", tt.in, err)55 continue56 }57 if got := Text(n); got != tt.want {58 t.Errorf("Text(Parse(%q)) = %q, want %q", tt.in, got, tt.want)59 }60 }61 // Names bound by LET are locals; the same name outside is a name.62 n, _ := Parse("=LET(x, 1, x) + x", bindFuncs)63 var names []string64 WalkNames(n, func(nn Name) { names = append(names, nn.Name) })65 if strings.Join(names, ",") != "x" {66 t.Errorf("names outside LET = %v, want the one x", names)67 }68 call := n.(Binary).L.(Call)69 if _, ok := call.Args[2].(Local); !ok {70 t.Errorf("x inside LET = %T, want Local", call.Args[2])71 }72 for _, tt := range []struct{ in, want string }{73 {"=LET(x, 1)", "Wrong number of arguments to LET"},74 {"=LET(1, 2, 3)", "Argument 1 of LET must be a name"},75 {"=LET(A1, 2, A1)", "Argument 1 of LET must be a name"},76 {"=LAMBDA(1, 2)", "Argument 1 of LAMBDA must be a name"},77 {"=f(1)", "Unknown function F"},78 } {79 _, err := Parse(tt.in, bindFuncs)80 if err == nil || !strings.HasPrefix(err.Error(), tt.want) {81 t.Errorf("Parse(%q) = %v, want %q", tt.in, err, tt.want)82 }83 }84}8586func TestRewriteArrays(t *testing.T) {87 n, err := Parse("={A1,B1;LET(x,C1,x),1}", bindFuncs)88 if err != nil {89 t.Fatal(err)90 }91 moved, ok := Rewrite(n, Shift(0, 1))92 if !ok || Text(moved) != "={A2,B2;LET(x,C2,x),1}" {93 t.Errorf("shifted = %s (%v)", Text(moved), ok)94 }95 if Text(n) != "={A1,B1;LET(x,C1,x),1}" {96 t.Errorf("the original changed: %s", Text(n))97 }98 var refs []string99 WalkRefs(n, func(_ string, a Addr) { refs = append(refs, a.String()) }, func(string, Rect) {})100 if strings.Join(refs, ",") != "A1,B1,C1" {101 t.Errorf("refs = %v", refs)102 }103}