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