1*4947cdc7SCole Faust// Copyright 2017 The Bazel Authors. All rights reserved. 2*4947cdc7SCole Faust// Use of this source code is governed by a BSD-style 3*4947cdc7SCole Faust// license that can be found in the LICENSE file. 4*4947cdc7SCole Faust 5*4947cdc7SCole Faustpackage syntax_test 6*4947cdc7SCole Faust 7*4947cdc7SCole Faustimport ( 8*4947cdc7SCole Faust "bufio" 9*4947cdc7SCole Faust "bytes" 10*4947cdc7SCole Faust "fmt" 11*4947cdc7SCole Faust "go/build" 12*4947cdc7SCole Faust "io/ioutil" 13*4947cdc7SCole Faust "path/filepath" 14*4947cdc7SCole Faust "reflect" 15*4947cdc7SCole Faust "strings" 16*4947cdc7SCole Faust "testing" 17*4947cdc7SCole Faust 18*4947cdc7SCole Faust "go.starlark.net/internal/chunkedfile" 19*4947cdc7SCole Faust "go.starlark.net/starlarktest" 20*4947cdc7SCole Faust "go.starlark.net/syntax" 21*4947cdc7SCole Faust) 22*4947cdc7SCole Faust 23*4947cdc7SCole Faustfunc TestExprParseTrees(t *testing.T) { 24*4947cdc7SCole Faust for _, test := range []struct { 25*4947cdc7SCole Faust input, want string 26*4947cdc7SCole Faust }{ 27*4947cdc7SCole Faust {`print(1)`, 28*4947cdc7SCole Faust `(CallExpr Fn=print Args=(1))`}, 29*4947cdc7SCole Faust {"print(1)\n", 30*4947cdc7SCole Faust `(CallExpr Fn=print Args=(1))`}, 31*4947cdc7SCole Faust {`x + 1`, 32*4947cdc7SCole Faust `(BinaryExpr X=x Op=+ Y=1)`}, 33*4947cdc7SCole Faust {`[x for x in y]`, 34*4947cdc7SCole Faust `(Comprehension Body=x Clauses=((ForClause Vars=x X=y)))`}, 35*4947cdc7SCole Faust {`[x for x in (a if b else c)]`, 36*4947cdc7SCole Faust `(Comprehension Body=x Clauses=((ForClause Vars=x X=(ParenExpr X=(CondExpr Cond=b True=a False=c)))))`}, 37*4947cdc7SCole Faust {`x[i].f(42)`, 38*4947cdc7SCole Faust `(CallExpr Fn=(DotExpr X=(IndexExpr X=x Y=i) Name=f) Args=(42))`}, 39*4947cdc7SCole Faust {`x.f()`, 40*4947cdc7SCole Faust `(CallExpr Fn=(DotExpr X=x Name=f))`}, 41*4947cdc7SCole Faust {`x+y*z`, 42*4947cdc7SCole Faust `(BinaryExpr X=x Op=+ Y=(BinaryExpr X=y Op=* Y=z))`}, 43*4947cdc7SCole Faust {`x%y-z`, 44*4947cdc7SCole Faust `(BinaryExpr X=(BinaryExpr X=x Op=% Y=y) Op=- Y=z)`}, 45*4947cdc7SCole Faust {`a + b not in c`, 46*4947cdc7SCole Faust `(BinaryExpr X=(BinaryExpr X=a Op=+ Y=b) Op=not in Y=c)`}, 47*4947cdc7SCole Faust {`lambda x, *args, **kwargs: None`, 48*4947cdc7SCole Faust `(LambdaExpr Params=(x (UnaryExpr Op=* X=args) (UnaryExpr Op=** X=kwargs)) Body=None)`}, 49*4947cdc7SCole Faust {`{"one": 1}`, 50*4947cdc7SCole Faust `(DictExpr List=((DictEntry Key="one" Value=1)))`}, 51*4947cdc7SCole Faust {`a[i]`, 52*4947cdc7SCole Faust `(IndexExpr X=a Y=i)`}, 53*4947cdc7SCole Faust {`a[i:]`, 54*4947cdc7SCole Faust `(SliceExpr X=a Lo=i)`}, 55*4947cdc7SCole Faust {`a[:j]`, 56*4947cdc7SCole Faust `(SliceExpr X=a Hi=j)`}, 57*4947cdc7SCole Faust {`a[::]`, 58*4947cdc7SCole Faust `(SliceExpr X=a)`}, 59*4947cdc7SCole Faust {`a[::k]`, 60*4947cdc7SCole Faust `(SliceExpr X=a Step=k)`}, 61*4947cdc7SCole Faust {`[]`, 62*4947cdc7SCole Faust `(ListExpr)`}, 63*4947cdc7SCole Faust {`[1]`, 64*4947cdc7SCole Faust `(ListExpr List=(1))`}, 65*4947cdc7SCole Faust {`[1,]`, 66*4947cdc7SCole Faust `(ListExpr List=(1))`}, 67*4947cdc7SCole Faust {`[1, 2]`, 68*4947cdc7SCole Faust `(ListExpr List=(1 2))`}, 69*4947cdc7SCole Faust {`()`, 70*4947cdc7SCole Faust `(TupleExpr)`}, 71*4947cdc7SCole Faust {`(4,)`, 72*4947cdc7SCole Faust `(ParenExpr X=(TupleExpr List=(4)))`}, 73*4947cdc7SCole Faust {`(4)`, 74*4947cdc7SCole Faust `(ParenExpr X=4)`}, 75*4947cdc7SCole Faust {`(4, 5)`, 76*4947cdc7SCole Faust `(ParenExpr X=(TupleExpr List=(4 5)))`}, 77*4947cdc7SCole Faust {`1, 2, 3`, 78*4947cdc7SCole Faust `(TupleExpr List=(1 2 3))`}, 79*4947cdc7SCole Faust {`1, 2,`, 80*4947cdc7SCole Faust `unparenthesized tuple with trailing comma`}, 81*4947cdc7SCole Faust {`{}`, 82*4947cdc7SCole Faust `(DictExpr)`}, 83*4947cdc7SCole Faust {`{"a": 1}`, 84*4947cdc7SCole Faust `(DictExpr List=((DictEntry Key="a" Value=1)))`}, 85*4947cdc7SCole Faust {`{"a": 1,}`, 86*4947cdc7SCole Faust `(DictExpr List=((DictEntry Key="a" Value=1)))`}, 87*4947cdc7SCole Faust {`{"a": 1, "b": 2}`, 88*4947cdc7SCole Faust `(DictExpr List=((DictEntry Key="a" Value=1) (DictEntry Key="b" Value=2)))`}, 89*4947cdc7SCole Faust {`{x: y for (x, y) in z}`, 90*4947cdc7SCole Faust `(Comprehension Curly Body=(DictEntry Key=x Value=y) Clauses=((ForClause Vars=(ParenExpr X=(TupleExpr List=(x y))) X=z)))`}, 91*4947cdc7SCole Faust {`{x: y for a in b if c}`, 92*4947cdc7SCole Faust `(Comprehension Curly Body=(DictEntry Key=x Value=y) Clauses=((ForClause Vars=a X=b) (IfClause Cond=c)))`}, 93*4947cdc7SCole Faust {`-1 + +2`, 94*4947cdc7SCole Faust `(BinaryExpr X=(UnaryExpr Op=- X=1) Op=+ Y=(UnaryExpr Op=+ X=2))`}, 95*4947cdc7SCole Faust {`"foo" + "bar"`, 96*4947cdc7SCole Faust `(BinaryExpr X="foo" Op=+ Y="bar")`}, 97*4947cdc7SCole Faust {`-1 * 2`, // prec(unary -) > prec(binary *) 98*4947cdc7SCole Faust `(BinaryExpr X=(UnaryExpr Op=- X=1) Op=* Y=2)`}, 99*4947cdc7SCole Faust {`-x[i]`, // prec(unary -) < prec(x[i]) 100*4947cdc7SCole Faust `(UnaryExpr Op=- X=(IndexExpr X=x Y=i))`}, 101*4947cdc7SCole Faust {`a | b & c | d`, // prec(|) < prec(&) 102*4947cdc7SCole Faust `(BinaryExpr X=(BinaryExpr X=a Op=| Y=(BinaryExpr X=b Op=& Y=c)) Op=| Y=d)`}, 103*4947cdc7SCole Faust {`a or b and c or d`, 104*4947cdc7SCole Faust `(BinaryExpr X=(BinaryExpr X=a Op=or Y=(BinaryExpr X=b Op=and Y=c)) Op=or Y=d)`}, 105*4947cdc7SCole Faust {`a and b or c and d`, 106*4947cdc7SCole Faust `(BinaryExpr X=(BinaryExpr X=a Op=and Y=b) Op=or Y=(BinaryExpr X=c Op=and Y=d))`}, 107*4947cdc7SCole Faust {`f(1, x=y)`, 108*4947cdc7SCole Faust `(CallExpr Fn=f Args=(1 (BinaryExpr X=x Op== Y=y)))`}, 109*4947cdc7SCole Faust {`f(*args, **kwargs)`, 110*4947cdc7SCole Faust `(CallExpr Fn=f Args=((UnaryExpr Op=* X=args) (UnaryExpr Op=** X=kwargs)))`}, 111*4947cdc7SCole Faust {`lambda *args, *, x=1, **kwargs: 0`, 112*4947cdc7SCole Faust `(LambdaExpr Params=((UnaryExpr Op=* X=args) (UnaryExpr Op=*) (BinaryExpr X=x Op== Y=1) (UnaryExpr Op=** X=kwargs)) Body=0)`}, 113*4947cdc7SCole Faust {`lambda *, a, *b: 0`, 114*4947cdc7SCole Faust `(LambdaExpr Params=((UnaryExpr Op=*) a (UnaryExpr Op=* X=b)) Body=0)`}, 115*4947cdc7SCole Faust {`a if b else c`, 116*4947cdc7SCole Faust `(CondExpr Cond=b True=a False=c)`}, 117*4947cdc7SCole Faust {`a and not b`, 118*4947cdc7SCole Faust `(BinaryExpr X=a Op=and Y=(UnaryExpr Op=not X=b))`}, 119*4947cdc7SCole Faust {`[e for x in y if cond1 if cond2]`, 120*4947cdc7SCole Faust `(Comprehension Body=e Clauses=((ForClause Vars=x X=y) (IfClause Cond=cond1) (IfClause Cond=cond2)))`}, // github.com/google/skylark/issues/53 121*4947cdc7SCole Faust } { 122*4947cdc7SCole Faust e, err := syntax.ParseExpr("foo.star", test.input, 0) 123*4947cdc7SCole Faust var got string 124*4947cdc7SCole Faust if err != nil { 125*4947cdc7SCole Faust got = stripPos(err) 126*4947cdc7SCole Faust } else { 127*4947cdc7SCole Faust got = treeString(e) 128*4947cdc7SCole Faust } 129*4947cdc7SCole Faust if test.want != got { 130*4947cdc7SCole Faust t.Errorf("parse `%s` = %s, want %s", test.input, got, test.want) 131*4947cdc7SCole Faust } 132*4947cdc7SCole Faust } 133*4947cdc7SCole Faust} 134*4947cdc7SCole Faust 135*4947cdc7SCole Faustfunc TestStmtParseTrees(t *testing.T) { 136*4947cdc7SCole Faust for _, test := range []struct { 137*4947cdc7SCole Faust input, want string 138*4947cdc7SCole Faust }{ 139*4947cdc7SCole Faust {`print(1)`, 140*4947cdc7SCole Faust `(ExprStmt X=(CallExpr Fn=print Args=(1)))`}, 141*4947cdc7SCole Faust {`return 1, 2`, 142*4947cdc7SCole Faust `(ReturnStmt Result=(TupleExpr List=(1 2)))`}, 143*4947cdc7SCole Faust {`return`, 144*4947cdc7SCole Faust `(ReturnStmt)`}, 145*4947cdc7SCole Faust {`for i in "abc": break`, 146*4947cdc7SCole Faust `(ForStmt Vars=i X="abc" Body=((BranchStmt Token=break)))`}, 147*4947cdc7SCole Faust {`for i in "abc": continue`, 148*4947cdc7SCole Faust `(ForStmt Vars=i X="abc" Body=((BranchStmt Token=continue)))`}, 149*4947cdc7SCole Faust {`for x, y in z: pass`, 150*4947cdc7SCole Faust `(ForStmt Vars=(TupleExpr List=(x y)) X=z Body=((BranchStmt Token=pass)))`}, 151*4947cdc7SCole Faust {`if True: pass`, 152*4947cdc7SCole Faust `(IfStmt Cond=True True=((BranchStmt Token=pass)))`}, 153*4947cdc7SCole Faust {`if True: break`, 154*4947cdc7SCole Faust `(IfStmt Cond=True True=((BranchStmt Token=break)))`}, 155*4947cdc7SCole Faust {`if True: continue`, 156*4947cdc7SCole Faust `(IfStmt Cond=True True=((BranchStmt Token=continue)))`}, 157*4947cdc7SCole Faust {`if True: pass 158*4947cdc7SCole Faustelse: 159*4947cdc7SCole Faust pass`, 160*4947cdc7SCole Faust `(IfStmt Cond=True True=((BranchStmt Token=pass)) False=((BranchStmt Token=pass)))`}, 161*4947cdc7SCole Faust {"if a: pass\nelif b: pass\nelse: pass", 162*4947cdc7SCole Faust `(IfStmt Cond=a True=((BranchStmt Token=pass)) False=((IfStmt Cond=b True=((BranchStmt Token=pass)) False=((BranchStmt Token=pass)))))`}, 163*4947cdc7SCole Faust {`x, y = 1, 2`, 164*4947cdc7SCole Faust `(AssignStmt Op== LHS=(TupleExpr List=(x y)) RHS=(TupleExpr List=(1 2)))`}, 165*4947cdc7SCole Faust {`x[i] = 1`, 166*4947cdc7SCole Faust `(AssignStmt Op== LHS=(IndexExpr X=x Y=i) RHS=1)`}, 167*4947cdc7SCole Faust {`x.f = 1`, 168*4947cdc7SCole Faust `(AssignStmt Op== LHS=(DotExpr X=x Name=f) RHS=1)`}, 169*4947cdc7SCole Faust {`(x, y) = 1`, 170*4947cdc7SCole Faust `(AssignStmt Op== LHS=(ParenExpr X=(TupleExpr List=(x y))) RHS=1)`}, 171*4947cdc7SCole Faust {`load("", "a", b="c")`, 172*4947cdc7SCole Faust `(LoadStmt Module="" From=(a c) To=(a b))`}, 173*4947cdc7SCole Faust {`if True: load("", "a", b="c")`, // load needn't be at toplevel 174*4947cdc7SCole Faust `(IfStmt Cond=True True=((LoadStmt Module="" From=(a c) To=(a b))))`}, 175*4947cdc7SCole Faust {`def f(x, *args, **kwargs): 176*4947cdc7SCole Faust pass`, 177*4947cdc7SCole Faust `(DefStmt Name=f Params=(x (UnaryExpr Op=* X=args) (UnaryExpr Op=** X=kwargs)) Body=((BranchStmt Token=pass)))`}, 178*4947cdc7SCole Faust {`def f(**kwargs, *args): pass`, 179*4947cdc7SCole Faust `(DefStmt Name=f Params=((UnaryExpr Op=** X=kwargs) (UnaryExpr Op=* X=args)) Body=((BranchStmt Token=pass)))`}, 180*4947cdc7SCole Faust {`def f(a, b, c=d): pass`, 181*4947cdc7SCole Faust `(DefStmt Name=f Params=(a b (BinaryExpr X=c Op== Y=d)) Body=((BranchStmt Token=pass)))`}, 182*4947cdc7SCole Faust {`def f(a, b=c, d): pass`, 183*4947cdc7SCole Faust `(DefStmt Name=f Params=(a (BinaryExpr X=b Op== Y=c) d) Body=((BranchStmt Token=pass)))`}, // TODO(adonovan): fix this 184*4947cdc7SCole Faust {`def f(): 185*4947cdc7SCole Faust def g(): 186*4947cdc7SCole Faust pass 187*4947cdc7SCole Faust pass 188*4947cdc7SCole Faustdef h(): 189*4947cdc7SCole Faust pass`, 190*4947cdc7SCole Faust `(DefStmt Name=f Body=((DefStmt Name=g Body=((BranchStmt Token=pass))) (BranchStmt Token=pass)))`}, 191*4947cdc7SCole Faust {"f();g()", 192*4947cdc7SCole Faust `(ExprStmt X=(CallExpr Fn=f))`}, 193*4947cdc7SCole Faust {"f();", 194*4947cdc7SCole Faust `(ExprStmt X=(CallExpr Fn=f))`}, 195*4947cdc7SCole Faust {"f();g()\n", 196*4947cdc7SCole Faust `(ExprStmt X=(CallExpr Fn=f))`}, 197*4947cdc7SCole Faust {"f();\n", 198*4947cdc7SCole Faust `(ExprStmt X=(CallExpr Fn=f))`}, 199*4947cdc7SCole Faust } { 200*4947cdc7SCole Faust f, err := syntax.Parse("foo.star", test.input, 0) 201*4947cdc7SCole Faust if err != nil { 202*4947cdc7SCole Faust t.Errorf("parse `%s` failed: %v", test.input, stripPos(err)) 203*4947cdc7SCole Faust continue 204*4947cdc7SCole Faust } 205*4947cdc7SCole Faust if got := treeString(f.Stmts[0]); test.want != got { 206*4947cdc7SCole Faust t.Errorf("parse `%s` = %s, want %s", test.input, got, test.want) 207*4947cdc7SCole Faust } 208*4947cdc7SCole Faust } 209*4947cdc7SCole Faust} 210*4947cdc7SCole Faust 211*4947cdc7SCole Faust// TestFileParseTrees tests sequences of statements, and particularly 212*4947cdc7SCole Faust// handling of indentation, newlines, line continuations, and blank lines. 213*4947cdc7SCole Faustfunc TestFileParseTrees(t *testing.T) { 214*4947cdc7SCole Faust for _, test := range []struct { 215*4947cdc7SCole Faust input, want string 216*4947cdc7SCole Faust }{ 217*4947cdc7SCole Faust {`x = 1 218*4947cdc7SCole Faustprint(x)`, 219*4947cdc7SCole Faust `(AssignStmt Op== LHS=x RHS=1) 220*4947cdc7SCole Faust(ExprStmt X=(CallExpr Fn=print Args=(x)))`}, 221*4947cdc7SCole Faust {"if cond:\n\tpass", 222*4947cdc7SCole Faust `(IfStmt Cond=cond True=((BranchStmt Token=pass)))`}, 223*4947cdc7SCole Faust {"if cond:\n\tpass\nelse:\n\tpass", 224*4947cdc7SCole Faust `(IfStmt Cond=cond True=((BranchStmt Token=pass)) False=((BranchStmt Token=pass)))`}, 225*4947cdc7SCole Faust {`def f(): 226*4947cdc7SCole Faust pass 227*4947cdc7SCole Faustpass 228*4947cdc7SCole Faust 229*4947cdc7SCole Faustpass`, 230*4947cdc7SCole Faust `(DefStmt Name=f Body=((BranchStmt Token=pass))) 231*4947cdc7SCole Faust(BranchStmt Token=pass) 232*4947cdc7SCole Faust(BranchStmt Token=pass)`}, 233*4947cdc7SCole Faust {`pass; pass`, 234*4947cdc7SCole Faust `(BranchStmt Token=pass) 235*4947cdc7SCole Faust(BranchStmt Token=pass)`}, 236*4947cdc7SCole Faust {"pass\npass", 237*4947cdc7SCole Faust `(BranchStmt Token=pass) 238*4947cdc7SCole Faust(BranchStmt Token=pass)`}, 239*4947cdc7SCole Faust {"pass\n\npass", 240*4947cdc7SCole Faust `(BranchStmt Token=pass) 241*4947cdc7SCole Faust(BranchStmt Token=pass)`}, 242*4947cdc7SCole Faust {`x = (1 + 243*4947cdc7SCole Faust2)`, 244*4947cdc7SCole Faust `(AssignStmt Op== LHS=x RHS=(ParenExpr X=(BinaryExpr X=1 Op=+ Y=2)))`}, 245*4947cdc7SCole Faust {`x = 1 \ 246*4947cdc7SCole Faust+ 2`, 247*4947cdc7SCole Faust `(AssignStmt Op== LHS=x RHS=(BinaryExpr X=1 Op=+ Y=2))`}, 248*4947cdc7SCole Faust } { 249*4947cdc7SCole Faust f, err := syntax.Parse("foo.star", test.input, 0) 250*4947cdc7SCole Faust if err != nil { 251*4947cdc7SCole Faust t.Errorf("parse `%s` failed: %v", test.input, stripPos(err)) 252*4947cdc7SCole Faust continue 253*4947cdc7SCole Faust } 254*4947cdc7SCole Faust var buf bytes.Buffer 255*4947cdc7SCole Faust for i, stmt := range f.Stmts { 256*4947cdc7SCole Faust if i > 0 { 257*4947cdc7SCole Faust buf.WriteByte('\n') 258*4947cdc7SCole Faust } 259*4947cdc7SCole Faust writeTree(&buf, reflect.ValueOf(stmt)) 260*4947cdc7SCole Faust } 261*4947cdc7SCole Faust if got := buf.String(); test.want != got { 262*4947cdc7SCole Faust t.Errorf("parse `%s` = %s, want %s", test.input, got, test.want) 263*4947cdc7SCole Faust } 264*4947cdc7SCole Faust } 265*4947cdc7SCole Faust} 266*4947cdc7SCole Faust 267*4947cdc7SCole Faust// TestCompoundStmt tests handling of REPL-style compound statements. 268*4947cdc7SCole Faustfunc TestCompoundStmt(t *testing.T) { 269*4947cdc7SCole Faust for _, test := range []struct { 270*4947cdc7SCole Faust input, want string 271*4947cdc7SCole Faust }{ 272*4947cdc7SCole Faust // blank lines 273*4947cdc7SCole Faust {"\n", 274*4947cdc7SCole Faust ``}, 275*4947cdc7SCole Faust {" \n", 276*4947cdc7SCole Faust ``}, 277*4947cdc7SCole Faust {"# comment\n", 278*4947cdc7SCole Faust ``}, 279*4947cdc7SCole Faust // simple statement 280*4947cdc7SCole Faust {"1\n", 281*4947cdc7SCole Faust `(ExprStmt X=1)`}, 282*4947cdc7SCole Faust {"print(1)\n", 283*4947cdc7SCole Faust `(ExprStmt X=(CallExpr Fn=print Args=(1)))`}, 284*4947cdc7SCole Faust {"1;2;3;\n", 285*4947cdc7SCole Faust `(ExprStmt X=1)(ExprStmt X=2)(ExprStmt X=3)`}, 286*4947cdc7SCole Faust {"f();g()\n", 287*4947cdc7SCole Faust `(ExprStmt X=(CallExpr Fn=f))(ExprStmt X=(CallExpr Fn=g))`}, 288*4947cdc7SCole Faust {"f();\n", 289*4947cdc7SCole Faust `(ExprStmt X=(CallExpr Fn=f))`}, 290*4947cdc7SCole Faust {"f(\n\n\n\n\n\n\n)\n", 291*4947cdc7SCole Faust `(ExprStmt X=(CallExpr Fn=f))`}, 292*4947cdc7SCole Faust // complex statements 293*4947cdc7SCole Faust {"def f():\n pass\n\n", 294*4947cdc7SCole Faust `(DefStmt Name=f Body=((BranchStmt Token=pass)))`}, 295*4947cdc7SCole Faust {"if cond:\n pass\n\n", 296*4947cdc7SCole Faust `(IfStmt Cond=cond True=((BranchStmt Token=pass)))`}, 297*4947cdc7SCole Faust // Even as a 1-liner, the following blank line is required. 298*4947cdc7SCole Faust {"if cond: pass\n\n", 299*4947cdc7SCole Faust `(IfStmt Cond=cond True=((BranchStmt Token=pass)))`}, 300*4947cdc7SCole Faust // github.com/google/starlark-go/issues/121 301*4947cdc7SCole Faust {"a; b; c\n", 302*4947cdc7SCole Faust `(ExprStmt X=a)(ExprStmt X=b)(ExprStmt X=c)`}, 303*4947cdc7SCole Faust {"a; b c\n", 304*4947cdc7SCole Faust `invalid syntax`}, 305*4947cdc7SCole Faust } { 306*4947cdc7SCole Faust 307*4947cdc7SCole Faust // Fake readline input from string. 308*4947cdc7SCole Faust // The ! suffix, which would cause a parse error, 309*4947cdc7SCole Faust // tests that the parser doesn't read more than necessary. 310*4947cdc7SCole Faust sc := bufio.NewScanner(strings.NewReader(test.input + "!")) 311*4947cdc7SCole Faust readline := func() ([]byte, error) { 312*4947cdc7SCole Faust if sc.Scan() { 313*4947cdc7SCole Faust return []byte(sc.Text() + "\n"), nil 314*4947cdc7SCole Faust } 315*4947cdc7SCole Faust return nil, sc.Err() 316*4947cdc7SCole Faust } 317*4947cdc7SCole Faust 318*4947cdc7SCole Faust var got string 319*4947cdc7SCole Faust f, err := syntax.ParseCompoundStmt("foo.star", readline) 320*4947cdc7SCole Faust if err != nil { 321*4947cdc7SCole Faust got = stripPos(err) 322*4947cdc7SCole Faust } else { 323*4947cdc7SCole Faust for _, stmt := range f.Stmts { 324*4947cdc7SCole Faust got += treeString(stmt) 325*4947cdc7SCole Faust } 326*4947cdc7SCole Faust } 327*4947cdc7SCole Faust if test.want != got { 328*4947cdc7SCole Faust t.Errorf("parse `%s` = %s, want %s", test.input, got, test.want) 329*4947cdc7SCole Faust } 330*4947cdc7SCole Faust } 331*4947cdc7SCole Faust} 332*4947cdc7SCole Faust 333*4947cdc7SCole Faustfunc stripPos(err error) string { 334*4947cdc7SCole Faust s := err.Error() 335*4947cdc7SCole Faust if i := strings.Index(s, ": "); i >= 0 { 336*4947cdc7SCole Faust s = s[i+len(": "):] // strip file:line:col 337*4947cdc7SCole Faust } 338*4947cdc7SCole Faust return s 339*4947cdc7SCole Faust} 340*4947cdc7SCole Faust 341*4947cdc7SCole Faust// treeString prints a syntax node as a parenthesized tree. 342*4947cdc7SCole Faust// Idents are printed as foo and Literals as "foo" or 42. 343*4947cdc7SCole Faust// Structs are printed as (type name=value ...). 344*4947cdc7SCole Faust// Only non-empty fields are shown. 345*4947cdc7SCole Faustfunc treeString(n syntax.Node) string { 346*4947cdc7SCole Faust var buf bytes.Buffer 347*4947cdc7SCole Faust writeTree(&buf, reflect.ValueOf(n)) 348*4947cdc7SCole Faust return buf.String() 349*4947cdc7SCole Faust} 350*4947cdc7SCole Faust 351*4947cdc7SCole Faustfunc writeTree(out *bytes.Buffer, x reflect.Value) { 352*4947cdc7SCole Faust switch x.Kind() { 353*4947cdc7SCole Faust case reflect.String, reflect.Int, reflect.Bool: 354*4947cdc7SCole Faust fmt.Fprintf(out, "%v", x.Interface()) 355*4947cdc7SCole Faust case reflect.Ptr, reflect.Interface: 356*4947cdc7SCole Faust if elem := x.Elem(); elem.Kind() == 0 { 357*4947cdc7SCole Faust out.WriteString("nil") 358*4947cdc7SCole Faust } else { 359*4947cdc7SCole Faust writeTree(out, elem) 360*4947cdc7SCole Faust } 361*4947cdc7SCole Faust case reflect.Struct: 362*4947cdc7SCole Faust switch v := x.Interface().(type) { 363*4947cdc7SCole Faust case syntax.Literal: 364*4947cdc7SCole Faust switch v.Token { 365*4947cdc7SCole Faust case syntax.STRING: 366*4947cdc7SCole Faust fmt.Fprintf(out, "%q", v.Value) 367*4947cdc7SCole Faust case syntax.BYTES: 368*4947cdc7SCole Faust fmt.Fprintf(out, "b%q", v.Value) 369*4947cdc7SCole Faust case syntax.INT: 370*4947cdc7SCole Faust fmt.Fprintf(out, "%d", v.Value) 371*4947cdc7SCole Faust } 372*4947cdc7SCole Faust return 373*4947cdc7SCole Faust case syntax.Ident: 374*4947cdc7SCole Faust out.WriteString(v.Name) 375*4947cdc7SCole Faust return 376*4947cdc7SCole Faust } 377*4947cdc7SCole Faust fmt.Fprintf(out, "(%s", strings.TrimPrefix(x.Type().String(), "syntax.")) 378*4947cdc7SCole Faust for i, n := 0, x.NumField(); i < n; i++ { 379*4947cdc7SCole Faust f := x.Field(i) 380*4947cdc7SCole Faust if f.Type() == reflect.TypeOf(syntax.Position{}) { 381*4947cdc7SCole Faust continue // skip positions 382*4947cdc7SCole Faust } 383*4947cdc7SCole Faust name := x.Type().Field(i).Name 384*4947cdc7SCole Faust if name == "commentsRef" { 385*4947cdc7SCole Faust continue // skip comments fields 386*4947cdc7SCole Faust } 387*4947cdc7SCole Faust if f.Type() == reflect.TypeOf(syntax.Token(0)) { 388*4947cdc7SCole Faust fmt.Fprintf(out, " %s=%s", name, f.Interface()) 389*4947cdc7SCole Faust continue 390*4947cdc7SCole Faust } 391*4947cdc7SCole Faust 392*4947cdc7SCole Faust switch f.Kind() { 393*4947cdc7SCole Faust case reflect.Slice: 394*4947cdc7SCole Faust if n := f.Len(); n > 0 { 395*4947cdc7SCole Faust fmt.Fprintf(out, " %s=(", name) 396*4947cdc7SCole Faust for i := 0; i < n; i++ { 397*4947cdc7SCole Faust if i > 0 { 398*4947cdc7SCole Faust out.WriteByte(' ') 399*4947cdc7SCole Faust } 400*4947cdc7SCole Faust writeTree(out, f.Index(i)) 401*4947cdc7SCole Faust } 402*4947cdc7SCole Faust out.WriteByte(')') 403*4947cdc7SCole Faust } 404*4947cdc7SCole Faust continue 405*4947cdc7SCole Faust case reflect.Ptr, reflect.Interface: 406*4947cdc7SCole Faust if f.IsNil() { 407*4947cdc7SCole Faust continue 408*4947cdc7SCole Faust } 409*4947cdc7SCole Faust case reflect.Int: 410*4947cdc7SCole Faust if f.Int() != 0 { 411*4947cdc7SCole Faust fmt.Fprintf(out, " %s=%d", name, f.Int()) 412*4947cdc7SCole Faust } 413*4947cdc7SCole Faust continue 414*4947cdc7SCole Faust case reflect.Bool: 415*4947cdc7SCole Faust if f.Bool() { 416*4947cdc7SCole Faust fmt.Fprintf(out, " %s", name) 417*4947cdc7SCole Faust } 418*4947cdc7SCole Faust continue 419*4947cdc7SCole Faust } 420*4947cdc7SCole Faust fmt.Fprintf(out, " %s=", name) 421*4947cdc7SCole Faust writeTree(out, f) 422*4947cdc7SCole Faust } 423*4947cdc7SCole Faust fmt.Fprintf(out, ")") 424*4947cdc7SCole Faust default: 425*4947cdc7SCole Faust fmt.Fprintf(out, "%T", x.Interface()) 426*4947cdc7SCole Faust } 427*4947cdc7SCole Faust} 428*4947cdc7SCole Faust 429*4947cdc7SCole Faustfunc TestParseErrors(t *testing.T) { 430*4947cdc7SCole Faust filename := starlarktest.DataFile("syntax", "testdata/errors.star") 431*4947cdc7SCole Faust for _, chunk := range chunkedfile.Read(filename, t) { 432*4947cdc7SCole Faust _, err := syntax.Parse(filename, chunk.Source, 0) 433*4947cdc7SCole Faust switch err := err.(type) { 434*4947cdc7SCole Faust case nil: 435*4947cdc7SCole Faust // ok 436*4947cdc7SCole Faust case syntax.Error: 437*4947cdc7SCole Faust chunk.GotError(int(err.Pos.Line), err.Msg) 438*4947cdc7SCole Faust default: 439*4947cdc7SCole Faust t.Error(err) 440*4947cdc7SCole Faust } 441*4947cdc7SCole Faust chunk.Done() 442*4947cdc7SCole Faust } 443*4947cdc7SCole Faust} 444*4947cdc7SCole Faust 445*4947cdc7SCole Faustfunc TestFilePortion(t *testing.T) { 446*4947cdc7SCole Faust // Imagine that the Starlark file or expression print(x.f) is extracted 447*4947cdc7SCole Faust // from the middle of a file in some hypothetical template language; 448*4947cdc7SCole Faust // see https://github.com/google/starlark-go/issues/346. For example: 449*4947cdc7SCole Faust // -- 450*4947cdc7SCole Faust // {{loop x seq}} 451*4947cdc7SCole Faust // {{print(x.f)}} 452*4947cdc7SCole Faust // {{end}} 453*4947cdc7SCole Faust // -- 454*4947cdc7SCole Faust fp := syntax.FilePortion{Content: []byte("print(x.f)"), FirstLine: 2, FirstCol: 4} 455*4947cdc7SCole Faust file, err := syntax.Parse("foo.template", fp, 0) 456*4947cdc7SCole Faust if err != nil { 457*4947cdc7SCole Faust t.Fatal(err) 458*4947cdc7SCole Faust } 459*4947cdc7SCole Faust span := fmt.Sprint(file.Stmts[0].Span()) 460*4947cdc7SCole Faust want := "foo.template:2:4 foo.template:2:14" 461*4947cdc7SCole Faust if span != want { 462*4947cdc7SCole Faust t.Errorf("wrong span: got %q, want %q", span, want) 463*4947cdc7SCole Faust } 464*4947cdc7SCole Faust} 465*4947cdc7SCole Faust 466*4947cdc7SCole Faust// dataFile is the same as starlarktest.DataFile. 467*4947cdc7SCole Faust// We make a copy to avoid a dependency cycle. 468*4947cdc7SCole Faustvar dataFile = func(pkgdir, filename string) string { 469*4947cdc7SCole Faust return filepath.Join(build.Default.GOPATH, "src/go.starlark.net", pkgdir, filename) 470*4947cdc7SCole Faust} 471*4947cdc7SCole Faust 472*4947cdc7SCole Faustfunc BenchmarkParse(b *testing.B) { 473*4947cdc7SCole Faust filename := dataFile("syntax", "testdata/scan.star") 474*4947cdc7SCole Faust b.StopTimer() 475*4947cdc7SCole Faust data, err := ioutil.ReadFile(filename) 476*4947cdc7SCole Faust if err != nil { 477*4947cdc7SCole Faust b.Fatal(err) 478*4947cdc7SCole Faust } 479*4947cdc7SCole Faust b.StartTimer() 480*4947cdc7SCole Faust 481*4947cdc7SCole Faust for i := 0; i < b.N; i++ { 482*4947cdc7SCole Faust _, err := syntax.Parse(filename, data, 0) 483*4947cdc7SCole Faust if err != nil { 484*4947cdc7SCole Faust b.Fatal(err) 485*4947cdc7SCole Faust } 486*4947cdc7SCole Faust } 487*4947cdc7SCole Faust} 488