···11+// Copyright 2018 The CUE Authors
22+//
33+// Licensed under the Apache License, Version 2.0 (the "License");
44+// you may not use this file except in compliance with the License.
55+// You may obtain a copy of the License at
66+//
77+// http://www.apache.org/licenses/LICENSE-2.0
88+//
99+// Unless required by applicable law or agreed to in writing, software
1010+// distributed under the License is distributed on an "AS IS" BASIS,
1111+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1212+// See the License for the specific language governing permissions and
1313+// limitations under the License.
1414+1515+// Package parser implements a parser for CUE source files. Input may be
1616+// provided in a variety of forms (see the various Parse* functions); the output
1717+// is an abstract syntax tree (AST) representing the CUE source. The parser is
1818+// invoked through one of the Parse* functions.
1919+//
2020+// The parser accepts a larger language than is syntactically permitted by the
2121+// CUE spec, for simplicity, and for improved robustness in the presence of
2222+// syntax errors.
2323+package parser // import "cuelang.org/go/cue/parser"
+198
cue/parser/error_test.go
···11+// Copyright 2018 The CUE Authors
22+//
33+// Licensed under the Apache License, Version 2.0 (the "License");
44+// you may not use this file except in compliance with the License.
55+// You may obtain a copy of the License at
66+//
77+// http://www.apache.org/licenses/LICENSE-2.0
88+//
99+// Unless required by applicable law or agreed to in writing, software
1010+// distributed under the License is distributed on an "AS IS" BASIS,
1111+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1212+// See the License for the specific language governing permissions and
1313+// limitations under the License.
1414+1515+// This file implements a parser test harness. The files in the testdata
1616+// directory are parsed and the errors reported are compared against the
1717+// error messages expected in the test files. The test files must end in
1818+// .src rather than .go so that they are not disturbed by gofmt runs.
1919+//
2020+// Expected errors are indicated in the test files by putting a comment
2121+// of the form /* ERROR "rx" */ immediately following an offending
2222+// The harness will verify that an error matching the regular expression
2323+// rx is reported at that source position.
2424+//
2525+// For instance, the following test file indicates that a "not declared"
2626+// error should be reported for the undeclared variable x:
2727+//
2828+// package p
2929+// {
3030+// a = x /* ERROR "not declared" */ + 1
3131+// }
3232+3333+package parser
3434+3535+import (
3636+ "io/ioutil"
3737+ "path/filepath"
3838+ "regexp"
3939+ "strings"
4040+ "testing"
4141+4242+ "cuelang.org/go/cue/errors"
4343+ "cuelang.org/go/cue/scanner"
4444+ "cuelang.org/go/cue/token"
4545+)
4646+4747+const testdata = "testdata"
4848+4949+// getFile assumes that each filename occurs at most once
5050+func getFile(fset *token.FileSet, filename string) (info *token.File) {
5151+ fset.Iterate(func(f *token.File) bool {
5252+ if f.Name() == filename {
5353+ if info != nil {
5454+ panic(filename + " used multiple times")
5555+ }
5656+ info = f
5757+ }
5858+ return true
5959+ })
6060+ return info
6161+}
6262+6363+func getPos(fset *token.FileSet, filename string, offset int) token.Pos {
6464+ if f := getFile(fset, filename); f != nil {
6565+ return f.Pos(offset, 0)
6666+ }
6767+ return token.NoPos
6868+}
6969+7070+// ERROR comments must be of the form /* ERROR "rx" */ and rx is
7171+// a regular expression that matches the expected error message.
7272+// The special form /* ERROR HERE "rx" */ must be used for error
7373+// messages that appear immediately after a token, rather than at
7474+// a token's position.
7575+//
7676+var errRx = regexp.MustCompile(`^/\* *ERROR *(HERE)? *"([^"]*)" *\*/$`)
7777+7878+// expectedErrors collects the regular expressions of ERROR comments found
7979+// in files and returns them as a map of error positions to error messages.
8080+//
8181+func expectedErrors(t *testing.T, fset *token.FileSet, filename string, src []byte) map[token.Pos]string {
8282+ errors := make(map[token.Pos]string)
8383+8484+ var s scanner.Scanner
8585+ // file was parsed already - do not add it again to the file
8686+ // set otherwise the position information returned here will
8787+ // not match the position information collected by the parser
8888+ s.Init(getFile(fset, filename), src, nil, scanner.ScanComments)
8989+ var prev token.Pos // position of last non-comment, non-semicolon token
9090+ var here token.Pos // position immediately after the token at position prev
9191+9292+ for {
9393+ pos, tok, lit := s.Scan()
9494+ pos = pos.WithRel(0)
9595+ switch tok {
9696+ case token.EOF:
9797+ return errors
9898+ case token.COMMENT:
9999+ s := errRx.FindStringSubmatch(lit)
100100+ if len(s) == 3 {
101101+ pos := prev
102102+ if s[1] == "HERE" {
103103+ pos = here
104104+ }
105105+ errors[pos] = string(s[2])
106106+ }
107107+ default:
108108+ prev = pos
109109+ var l int // token length
110110+ if tok.IsLiteral() {
111111+ l = len(lit)
112112+ } else {
113113+ l = len(tok.String())
114114+ }
115115+ here = prev + token.Pos(l)
116116+ }
117117+ }
118118+}
119119+120120+// compareErrors compares the map of expected error messages with the list
121121+// of found errors and reports discrepancies.
122122+//
123123+func compareErrors(t *testing.T, fset *token.FileSet, expected map[token.Pos]string, found errors.List) {
124124+ t.Helper()
125125+ for _, error := range found {
126126+ // error.Pos is a Position, but we want
127127+ // a Pos so we can do a map lookup
128128+ ePos := error.Position()
129129+ eMsg := error.Error()
130130+ pos := getPos(fset, ePos.Filename, ePos.Offset).WithRel(0)
131131+ if msg, found := expected[pos]; found {
132132+ // we expect a message at pos; check if it matches
133133+ rx, err := regexp.Compile(msg)
134134+ if err != nil {
135135+ t.Errorf("%s: %v", ePos, err)
136136+ continue
137137+ }
138138+ if match := rx.MatchString(eMsg); !match {
139139+ t.Errorf("%s: %q does not match %q", ePos, eMsg, msg)
140140+ continue
141141+ }
142142+ // we have a match - eliminate this error
143143+ delete(expected, pos)
144144+ } else {
145145+ // To keep in mind when analyzing failed test output:
146146+ // If the same error position occurs multiple times in errors,
147147+ // this message will be triggered (because the first error at
148148+ // the position removes this position from the expected errors).
149149+ t.Errorf("%s: unexpected error: -%q-", ePos, eMsg)
150150+ }
151151+ }
152152+153153+ // there should be no expected errors left
154154+ if len(expected) > 0 {
155155+ t.Errorf("%d errors not reported:", len(expected))
156156+ for pos, msg := range expected {
157157+ t.Errorf("%s: -%q-\n", fset.Position(pos), msg)
158158+ }
159159+ }
160160+}
161161+162162+func checkErrors(t *testing.T, filename string, input interface{}) {
163163+ t.Helper()
164164+ src, err := readSource(filename, input)
165165+ if err != nil {
166166+ t.Error(err)
167167+ return
168168+ }
169169+170170+ fset := token.NewFileSet()
171171+ _, err = ParseFile(fset, filename, src, DeclarationErrors, AllErrors, ParseLambdas)
172172+ found, ok := err.(errors.List)
173173+ if err != nil && !ok {
174174+ t.Error(err)
175175+ return
176176+ }
177177+ found.RemoveMultiples()
178178+179179+ // we are expecting the following errors
180180+ // (collect these after parsing a file so that it is found in the file set)
181181+ expected := expectedErrors(t, fset, filename, src)
182182+183183+ // verify errors returned by the parser
184184+ compareErrors(t, fset, expected, found)
185185+}
186186+187187+func TestErrors(t *testing.T) {
188188+ list, err := ioutil.ReadDir(testdata)
189189+ if err != nil {
190190+ t.Fatal(err)
191191+ }
192192+ for _, fi := range list {
193193+ name := fi.Name()
194194+ if !fi.IsDir() && !strings.HasPrefix(name, ".") && strings.HasSuffix(name, ".src") {
195195+ checkErrors(t, filepath.Join(testdata, name), nil)
196196+ }
197197+ }
198198+}
+41
cue/parser/example_test.go
···11+// Copyright 2018 The CUE Authors
22+//
33+// Licensed under the Apache License, Version 2.0 (the "License");
44+// you may not use this file except in compliance with the License.
55+// You may obtain a copy of the License at
66+//
77+// http://www.apache.org/licenses/LICENSE-2.0
88+//
99+// Unless required by applicable law or agreed to in writing, software
1010+// distributed under the License is distributed on an "AS IS" BASIS,
1111+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1212+// See the License for the specific language governing permissions and
1313+// limitations under the License.
1414+1515+package parser_test
1616+1717+import (
1818+ "fmt"
1919+2020+ "cuelang.org/go/cue/parser"
2121+ "cuelang.org/go/cue/token"
2222+)
2323+2424+func ExampleParseFile() {
2525+ fset := token.NewFileSet() // positions are relative to fset
2626+2727+ // Parse the file containing this very example
2828+ // but stop after processing the imports.
2929+ f, err := parser.ParseFile(fset, "testdata/test.cue", nil)
3030+ if err != nil {
3131+ fmt.Println(err)
3232+ return
3333+ }
3434+3535+ // Print the imports from the file's AST.
3636+ for _, s := range f.Imports {
3737+ fmt.Println(s.Path.Value)
3838+ }
3939+ // Output:
4040+ // "math"
4141+}
+169
cue/parser/import.go
···11+// Copyright 2018 The CUE Authors
22+//
33+// Licensed under the Apache License, Version 2.0 (the "License");
44+// you may not use this file except in compliance with the License.
55+// You may obtain a copy of the License at
66+//
77+// http://www.apache.org/licenses/LICENSE-2.0
88+//
99+// Unless required by applicable law or agreed to in writing, software
1010+// distributed under the License is distributed on an "AS IS" BASIS,
1111+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1212+// See the License for the specific language governing permissions and
1313+// limitations under the License.
1414+1515+package parser
1616+1717+import (
1818+ "sort"
1919+ "strconv"
2020+2121+ "cuelang.org/go/cue/ast"
2222+ "cuelang.org/go/cue/token"
2323+)
2424+2525+// sortImports sorts runs of consecutive import lines in import blocks in f.
2626+// It also removes duplicate imports when it is possible to do so without data loss.
2727+func sortImports(fset *token.FileSet, f *ast.File) {
2828+ for _, d := range f.Decls {
2929+ d, ok := d.(*ast.ImportDecl)
3030+ if !ok {
3131+ // Not an import declaration, so we're done.
3232+ // Imports are always first.
3333+ break
3434+ }
3535+3636+ if !d.Lparen.IsValid() {
3737+ // Not a block: sorted by default.
3838+ continue
3939+ }
4040+4141+ // Identify and sort runs of specs on successive lines.
4242+ i := 0
4343+ specs := d.Specs[:0]
4444+ for j, s := range d.Specs {
4545+ if j > i && fset.Position(s.Pos()).Line > 1+fset.Position(d.Specs[j-1].End()).Line {
4646+ // j begins a new run. End this one.
4747+ specs = append(specs, sortSpecs(fset, f, d.Specs[i:j])...)
4848+ i = j
4949+ }
5050+ }
5151+ specs = append(specs, sortSpecs(fset, f, d.Specs[i:])...)
5252+ d.Specs = specs
5353+5454+ // Deduping can leave a blank line before the rparen; clean that up.
5555+ if len(d.Specs) > 0 {
5656+ lastSpec := d.Specs[len(d.Specs)-1]
5757+ lastLine := fset.Position(lastSpec.Pos()).Line
5858+ rParenLine := fset.Position(d.Rparen).Line
5959+ for rParenLine > lastLine+1 {
6060+ rParenLine--
6161+ fset.File(d.Rparen).MergeLine(rParenLine)
6262+ }
6363+ }
6464+ }
6565+}
6666+6767+func importPath(s *ast.ImportSpec) string {
6868+ t, err := strconv.Unquote(s.Path.Value)
6969+ if err == nil {
7070+ return t
7171+ }
7272+ return ""
7373+}
7474+7575+func importName(s *ast.ImportSpec) string {
7676+ n := s.Name
7777+ if n == nil {
7878+ return ""
7979+ }
8080+ return n.Name
8181+}
8282+8383+func importComment(s *ast.ImportSpec) string {
8484+ for _, c := range s.Comments() {
8585+ if c.Line {
8686+ return c.Text()
8787+ }
8888+ }
8989+ return ""
9090+}
9191+9292+// collapse indicates whether prev may be removed, leaving only next.
9393+func collapse(prev, next *ast.ImportSpec) bool {
9494+ if importPath(next) != importPath(prev) || importName(next) != importName(prev) {
9595+ return false
9696+ }
9797+ for _, c := range prev.Comments() {
9898+ if !c.Doc {
9999+ return false
100100+ }
101101+ }
102102+ return true
103103+}
104104+105105+type posSpan struct {
106106+ Start token.Pos
107107+ End token.Pos
108108+}
109109+110110+func sortSpecs(fset *token.FileSet, f *ast.File, specs []*ast.ImportSpec) []*ast.ImportSpec {
111111+ // Can't short-circuit here even if specs are already sorted,
112112+ // since they might yet need deduplication.
113113+ // A lone import, however, may be safely ignored.
114114+ if len(specs) <= 1 {
115115+ return specs
116116+ }
117117+118118+ // Record positions for specs.
119119+ pos := make([]posSpan, len(specs))
120120+ for i, s := range specs {
121121+ pos[i] = posSpan{s.Pos(), s.End()}
122122+ }
123123+124124+ // Sort the import specs by import path.
125125+ // Remove duplicates, when possible without data loss.
126126+ // Reassign the import paths to have the same position sequence.
127127+ // Reassign each comment to abut the end of its spec.
128128+ // Sort the comments by new position.
129129+ sort.Sort(byImportSpec(specs))
130130+131131+ // Dedup. Thanks to our sorting, we can just consider
132132+ // adjacent pairs of imports.
133133+ deduped := specs[:0]
134134+ for i, s := range specs {
135135+ if i == len(specs)-1 || !collapse(s, specs[i+1]) {
136136+ deduped = append(deduped, s)
137137+ } else {
138138+ p := s.Pos()
139139+ fset.File(p).MergeLine(fset.Position(p).Line)
140140+ }
141141+ }
142142+ specs = deduped
143143+144144+ return specs
145145+}
146146+147147+type byImportSpec []*ast.ImportSpec
148148+149149+func (x byImportSpec) Len() int { return len(x) }
150150+func (x byImportSpec) Swap(i, j int) { x[i], x[j] = x[j], x[i] }
151151+func (x byImportSpec) Less(i, j int) bool {
152152+ ipath := importPath(x[i])
153153+ jpath := importPath(x[j])
154154+ if ipath != jpath {
155155+ return ipath < jpath
156156+ }
157157+ iname := importName(x[i])
158158+ jname := importName(x[j])
159159+ if iname != jname {
160160+ return iname < jname
161161+ }
162162+ return importComment(x[i]) < importComment(x[j])
163163+}
164164+165165+type byCommentPos []*ast.CommentGroup
166166+167167+func (x byCommentPos) Len() int { return len(x) }
168168+func (x byCommentPos) Swap(i, j int) { x[i], x[j] = x[j], x[i] }
169169+func (x byCommentPos) Less(i, j int) bool { return x[i].Pos() < x[j].Pos() }
+250
cue/parser/interface.go
···11+// Copyright 2018 The CUE Authors
22+//
33+// Licensed under the Apache License, Version 2.0 (the "License");
44+// you may not use this file except in compliance with the License.
55+// You may obtain a copy of the License at
66+//
77+// http://www.apache.org/licenses/LICENSE-2.0
88+//
99+// Unless required by applicable law or agreed to in writing, software
1010+// distributed under the License is distributed on an "AS IS" BASIS,
1111+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1212+// See the License for the specific language governing permissions and
1313+// limitations under the License.
1414+1515+// This file contains the exported entry points for invoking the
1616+1717+package parser
1818+1919+import (
2020+ "bytes"
2121+ "fmt"
2222+ "io"
2323+ "io/ioutil"
2424+2525+ "cuelang.org/go/cue/ast"
2626+ "cuelang.org/go/cue/token"
2727+)
2828+2929+// If src != nil, readSource converts src to a []byte if possible;
3030+// otherwise it returns an error. If src == nil, readSource returns
3131+// the result of reading the file specified by filename.
3232+//
3333+func readSource(filename string, src interface{}) ([]byte, error) {
3434+ if src != nil {
3535+ switch s := src.(type) {
3636+ case string:
3737+ return []byte(s), nil
3838+ case []byte:
3939+ return s, nil
4040+ case *bytes.Buffer:
4141+ // is io.Reader, but src is already available in []byte form
4242+ if s != nil {
4343+ return s.Bytes(), nil
4444+ }
4545+ case io.Reader:
4646+ var buf bytes.Buffer
4747+ if _, err := io.Copy(&buf, s); err != nil {
4848+ return nil, err
4949+ }
5050+ return buf.Bytes(), nil
5151+ }
5252+ return nil, fmt.Errorf("invalid source type %T", src)
5353+ }
5454+ return ioutil.ReadFile(filename)
5555+}
5656+5757+type Option func(p *parser)
5858+5959+var (
6060+ // PackageClauseOnly causes parsing to stop after the package clause.
6161+ PackageClauseOnly Option = packageClauseOnly
6262+ packageClauseOnly = func(p *parser) {
6363+ p.mode |= packageClauseOnlyMode
6464+ }
6565+6666+ // ImportsOnly causes parsing to stop parsing after the import declarations.
6767+ ImportsOnly Option = importsOnly
6868+ importsOnly = func(p *parser) {
6969+ p.mode |= importsOnlyMode
7070+ }
7171+7272+ // ParseComments causes comments to be parsed.
7373+ ParseComments Option = parseComments
7474+ parseComments = func(p *parser) {
7575+ p.mode |= parseCommentsMode
7676+ }
7777+7878+ // ParseLambdas enables parsing of Lambdas. By default these are disabled.
7979+ //
8080+ // NOTE: this option is for internal use only and can be made unavailable at
8181+ // any time.
8282+ ParseLambdas Option = parseLambdas
8383+ parseLambdas = func(p *parser) {
8484+ p.mode |= parseLambdasMode
8585+ }
8686+8787+ // Trace causes parsing to print a trace of parsed productions.
8888+ Trace Option = traceOpt
8989+ traceOpt = func(p *parser) {
9090+ p.mode |= traceMode
9191+ }
9292+9393+ // DeclarationErrors causes parsing to report declaration errors.
9494+ DeclarationErrors Option = declarationErrors
9595+ declarationErrors = func(p *parser) {
9696+ p.mode |= declarationErrorsMode
9797+ }
9898+9999+ // AllErrors causes all errors to be reported (not just the first 10 on different lines).
100100+ AllErrors Option = allErrors
101101+ allErrors = func(p *parser) {
102102+ p.mode |= allErrorsMode
103103+ }
104104+105105+ // AllowPartial allows the parser to be used on a prefix buffer.
106106+ AllowPartial Option = allowPartial
107107+ allowPartial = func(p *parser) {
108108+ p.mode |= partialMode
109109+ }
110110+)
111111+112112+// A mode value is a set of flags (or 0).
113113+// They control the amount of source code parsed and other optional
114114+// parser functionality.
115115+type mode uint
116116+117117+const (
118118+ packageClauseOnlyMode mode = 1 << iota // stop parsing after package clause
119119+ importsOnlyMode // stop parsing after import declarations
120120+ parseCommentsMode // parse comments and add them to AST
121121+ parseLambdasMode
122122+ partialMode
123123+ traceMode // print a trace of parsed productions
124124+ declarationErrorsMode // report declaration errors
125125+ allErrorsMode // report all errors (not just the first 10 on different lines)
126126+)
127127+128128+// ParseFile parses the source code of a single CUE source file and returns
129129+// the corresponding File node. The source code may be provided via
130130+// the filename of the source file, or via the src parameter.
131131+//
132132+// If src != nil, ParseFile parses the source from src and the filename is
133133+// only used when recording position information. The type of the argument
134134+// for the src parameter must be string, []byte, or io.Reader.
135135+// If src == nil, ParseFile parses the file specified by filename.
136136+//
137137+// The mode parameter controls the amount of source text parsed and other
138138+// optional parser functionality. Position information is recorded in the
139139+// file set fset, which must not be nil.
140140+//
141141+// If the source couldn't be read, the returned AST is nil and the error
142142+// indicates the specific failure. If the source was read but syntax
143143+// errors were found, the result is a partial AST (with Bad* nodes
144144+// representing the fragments of erroneous source code). Multiple errors
145145+// are returned via a ErrorList which is sorted by file position.
146146+func ParseFile(p *token.FileSet, filename string, src interface{}, mode ...Option) (f *ast.File, err error) {
147147+ if p == nil {
148148+ panic("ParseFile: no file.FileSet provided (fset == nil)")
149149+ }
150150+151151+ // get source
152152+ text, err := readSource(filename, src)
153153+ if err != nil {
154154+ return nil, err
155155+ }
156156+157157+ var pp parser
158158+ defer func() {
159159+ if e := recover(); e != nil {
160160+ // resume same panic if it's not a bailout
161161+ if _, ok := e.(bailout); !ok {
162162+ panic(e)
163163+ }
164164+ }
165165+166166+ // set result values
167167+ if f == nil {
168168+ // source is not a valid Go source file - satisfy
169169+ // ParseFile API and return a valid (but) empty
170170+ // *File
171171+ f = &ast.File{
172172+ Name: new(ast.Ident),
173173+ // Scope: NewScope(nil),
174174+ }
175175+ }
176176+177177+ pp.errors.Sort()
178178+ err = pp.errors.Err()
179179+ }()
180180+181181+ // parse source
182182+ pp.init(p, filename, text, mode)
183183+ f = pp.parseFile()
184184+ if f == nil {
185185+ return nil, pp.errors
186186+ }
187187+ f.Filename = filename
188188+ resolve(f, pp.error)
189189+190190+ return
191191+}
192192+193193+// ParseExpr is a convenience function for parsing an expression.
194194+// The arguments have the same meaning as for Parse, but the source must
195195+// be a valid CUE (type or value) expression. Specifically, fset must not
196196+// be nil.
197197+func ParseExpr(fset *token.FileSet, filename string, src interface{}, mode ...Option) (ast.Expr, error) {
198198+ if fset == nil {
199199+ panic("ParseExprFrom: no file.FileSet provided (fset == nil)")
200200+ }
201201+202202+ // get source
203203+ text, err := readSource(filename, src)
204204+ if err != nil {
205205+ return nil, err
206206+ }
207207+208208+ var p parser
209209+ defer func() {
210210+ if e := recover(); e != nil {
211211+ // resume same panic if it's not a bailout
212212+ if _, ok := e.(bailout); !ok {
213213+ panic(e)
214214+ }
215215+ }
216216+ p.errors.Sort()
217217+ err = p.errors.Err()
218218+ }()
219219+220220+ // parse expr
221221+ p.init(fset, filename, text, mode)
222222+ // Set up pkg-level scopes to avoid nil-pointer errors.
223223+ // This is not needed for a correct expression x as the
224224+ // parser will be ok with a nil topScope, but be cautious
225225+ // in case of an erroneous x.
226226+ e := p.parseRHS()
227227+228228+ // If a comma was inserted, consume it;
229229+ // report an error if there's more tokens.
230230+ if p.tok == token.COMMA && p.lit == "\n" {
231231+ p.next()
232232+ }
233233+ if p.mode&partialMode == 0 {
234234+ p.expect(token.EOF)
235235+ }
236236+237237+ if p.errors.Len() > 0 {
238238+ p.errors.Sort()
239239+ return nil, p.errors.Err()
240240+ }
241241+242242+ return e, nil
243243+}
244244+245245+// parseExprString is a convenience function for obtaining the AST of an
246246+// expression x. The position information recorded in the AST is undefined. The
247247+// filename used in error messages is the empty string.
248248+func parseExprString(x string) (ast.Expr, error) {
249249+ return ParseExpr(token.NewFileSet(), "", []byte(x))
250250+}
+126
cue/parser/interface_test.go
···11+// Copyright 2018 The CUE Authors
22+//
33+// Licensed under the Apache License, Version 2.0 (the "License");
44+// you may not use this file except in compliance with the License.
55+// You may obtain a copy of the License at
66+//
77+// http://www.apache.org/licenses/LICENSE-2.0
88+//
99+// Unless required by applicable law or agreed to in writing, software
1010+// distributed under the License is distributed on an "AS IS" BASIS,
1111+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1212+// See the License for the specific language governing permissions and
1313+// limitations under the License.
1414+1515+package parser
1616+1717+import (
1818+ "reflect"
1919+ "testing"
2020+2121+ "cuelang.org/go/cue/ast"
2222+ "cuelang.org/go/cue/token"
2323+)
2424+2525+func Test_readSource(t *testing.T) {
2626+ type args struct {
2727+ filename string
2828+ src interface{}
2929+ }
3030+ tests := []struct {
3131+ name string
3232+ args args
3333+ want []byte
3434+ wantErr bool
3535+ }{
3636+ // TODO: Add test cases.
3737+ }
3838+ for _, tt := range tests {
3939+ got, err := readSource(tt.args.filename, tt.args.src)
4040+ if (err != nil) != tt.wantErr {
4141+ t.Errorf("%q. readSource() error = %v, wantErr %v", tt.name, err, tt.wantErr)
4242+ continue
4343+ }
4444+ if !reflect.DeepEqual(got, tt.want) {
4545+ t.Errorf("%q. readSource() = %v, want %v", tt.name, got, tt.want)
4646+ }
4747+ }
4848+}
4949+5050+func TestParseFile(t *testing.T) {
5151+ type args struct {
5252+ fset *token.FileSet
5353+ filename string
5454+ src interface{}
5555+ options []Option
5656+ }
5757+ tests := []struct {
5858+ name string
5959+ args args
6060+ wantF *ast.File
6161+ wantErr bool
6262+ }{
6363+ // TODO: Add test cases.
6464+ }
6565+ for _, tt := range tests {
6666+ gotF, err := ParseFile(tt.args.fset, tt.args.filename, tt.args.src, tt.args.options...)
6767+ if (err != nil) != tt.wantErr {
6868+ t.Errorf("%q. ParseFile() error = %v, wantErr %v", tt.name, err, tt.wantErr)
6969+ continue
7070+ }
7171+ if !reflect.DeepEqual(gotF, tt.wantF) {
7272+ t.Errorf("%q. ParseFile() = %v, want %v", tt.name, gotF, tt.wantF)
7373+ }
7474+ }
7575+}
7676+7777+func TestParseExprFrom(t *testing.T) {
7878+ type args struct {
7979+ fset *token.FileSet
8080+ filename string
8181+ src interface{}
8282+ mode Option
8383+ }
8484+ tests := []struct {
8585+ name string
8686+ args args
8787+ want ast.Expr
8888+ wantErr bool
8989+ }{
9090+ // TODO: Add test cases.
9191+ }
9292+ for _, tt := range tests {
9393+ got, err := ParseExpr(tt.args.fset, tt.args.filename, tt.args.src, tt.args.mode)
9494+ if (err != nil) != tt.wantErr {
9595+ t.Errorf("%q. ParseExprFrom() error = %v, wantErr %v", tt.name, err, tt.wantErr)
9696+ continue
9797+ }
9898+ if !reflect.DeepEqual(got, tt.want) {
9999+ t.Errorf("%q. ParseExprFrom() = %v, want %v", tt.name, got, tt.want)
100100+ }
101101+ }
102102+}
103103+104104+func TestParseExprString(t *testing.T) {
105105+ type args struct {
106106+ x string
107107+ }
108108+ tests := []struct {
109109+ name string
110110+ args args
111111+ want ast.Expr
112112+ wantErr bool
113113+ }{
114114+ // TODO: Add test cases.
115115+ }
116116+ for _, tt := range tests {
117117+ got, err := parseExprString(tt.args.x)
118118+ if (err != nil) != tt.wantErr {
119119+ t.Errorf("%q. ParseExpr() error = %v, wantErr %v", tt.name, err, tt.wantErr)
120120+ continue
121121+ }
122122+ if !reflect.DeepEqual(got, tt.want) {
123123+ t.Errorf("%q. ParseExpr() = %v, want %v", tt.name, got, tt.want)
124124+ }
125125+ }
126126+}
+1446
cue/parser/parser.go
···11+// Copyright 2018 The CUE Authors
22+//
33+// Licensed under the Apache License, Version 2.0 (the "License");
44+// you may not use this file except in compliance with the License.
55+// You may obtain a copy of the License at
66+//
77+// http://www.apache.org/licenses/LICENSE-2.0
88+//
99+// Unless required by applicable law or agreed to in writing, software
1010+// distributed under the License is distributed on an "AS IS" BASIS,
1111+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1212+// See the License for the specific language governing permissions and
1313+// limitations under the License.
1414+1515+package parser
1616+1717+import (
1818+ "fmt"
1919+ "strconv"
2020+ "strings"
2121+ "unicode"
2222+2323+ "cuelang.org/go/cue/ast"
2424+ "cuelang.org/go/cue/errors"
2525+ "cuelang.org/go/cue/scanner"
2626+ "cuelang.org/go/cue/token"
2727+)
2828+2929+// The parser structure holds the parser's internal state.
3030+type parser struct {
3131+ file *token.File
3232+ errors errors.List
3333+ scanner scanner.Scanner
3434+3535+ // Tracing/debugging
3636+ mode mode // parsing mode
3737+ trace bool // == (mode & Trace != 0)
3838+ indent int // indentation used for tracing output
3939+4040+ // Comments
4141+ leadComment *ast.CommentGroup
4242+ comments *commentState
4343+4444+ // Next token
4545+ pos token.Pos // token position
4646+ tok token.Token // one token look-ahead
4747+ lit string // token literal
4848+4949+ // Error recovery
5050+ // (used to limit the number of calls to syncXXX functions
5151+ // w/o making scanning progress - avoids potential endless
5252+ // loops across multiple parser functions during error recovery)
5353+ syncPos token.Pos // last synchronization position
5454+ syncCnt int // number of calls to syncXXX without progress
5555+5656+ // Non-syntactic parser control
5757+ exprLev int // < 0: in control clause, >= 0: in expression
5858+5959+ imports []*ast.ImportSpec // list of imports
6060+6161+}
6262+6363+func (p *parser) init(fset *token.FileSet, filename string, src []byte, mode []Option) {
6464+ p.file = fset.AddFile(filename, -1, len(src))
6565+ for _, f := range mode {
6666+ f(p)
6767+ }
6868+ var m scanner.Mode
6969+ if p.mode&parseCommentsMode != 0 {
7070+ m = scanner.ScanComments
7171+ }
7272+ eh := func(pos token.Position, msg string) { p.errors.AddNew(pos, msg) }
7373+ p.scanner.Init(p.file, src, eh, m)
7474+7575+ p.trace = p.mode&traceMode != 0 // for convenience (p.trace is used frequently)
7676+7777+ p.comments = &commentState{pos: -1}
7878+7979+ p.next()
8080+}
8181+8282+type commentList struct {
8383+ taken bool // for validation
8484+ attachTail bool
8585+ head *ast.CommentGroup
8686+ last *ast.CommentGroup
8787+}
8888+8989+type commentState struct {
9090+ parent *commentState
9191+ pos int8
9292+ groups []*ast.CommentGroup
9393+9494+ // lists are not attached to nodes themselves. Enclosed expressions may
9595+ // miss a comment due to commas and line termination. closeLists ensures
9696+ // that comments will be passed to someone.
9797+ isList int
9898+ lastChild ast.Node
9999+ lastPos int8
100100+}
101101+102102+// openComments reserves the next doc comment for the caller and flushes
103103+func (p *parser) openComments() *commentState {
104104+ if c := p.comments; c != nil && c.isList > 0 {
105105+ if c.lastChild != nil {
106106+ for _, cg := range c.groups {
107107+ cg.Position = c.lastPos
108108+ c.lastChild.AddComment(cg)
109109+ }
110110+ c.groups = nil
111111+ }
112112+ c.lastChild = nil
113113+ }
114114+ c := &commentState{
115115+ parent: p.comments,
116116+ groups: []*ast.CommentGroup{p.leadComment},
117117+ }
118118+ p.comments = c
119119+ p.leadComment = nil
120120+ return c
121121+}
122122+123123+// openList is used to treat a list of comments as a single comment
124124+// position in a production.
125125+func (p *parser) openList() {
126126+ if p.comments.isList > 0 {
127127+ p.comments.isList++
128128+ return
129129+ }
130130+ c := &commentState{
131131+ parent: p.comments,
132132+ isList: 1,
133133+ }
134134+ p.comments = c
135135+}
136136+137137+func (c *commentState) add(g *ast.CommentGroup) {
138138+ g.Position = c.pos
139139+ c.groups = append(c.groups, g)
140140+}
141141+142142+func (p *parser) closeList() {
143143+ c := p.comments
144144+ if c.lastChild != nil {
145145+ for _, cg := range c.groups {
146146+ cg.Position = c.lastPos
147147+ c.lastChild.AddComment(cg)
148148+ }
149149+ c.groups = nil
150150+ }
151151+ switch c.isList--; {
152152+ case c.isList < 0:
153153+ panic("unmatched close list")
154154+ case c.isList == 0:
155155+ parent := c.parent
156156+ parent.groups = append(parent.groups, c.groups...)
157157+ parent.pos++
158158+ p.comments = parent
159159+ }
160160+}
161161+162162+func (c *commentState) closeNode(p *parser, n ast.Node) ast.Node {
163163+ if p.comments != c {
164164+ panic("unmatched comments")
165165+ }
166166+ p.comments = c.parent
167167+ if c.parent != nil {
168168+ c.parent.lastChild = n
169169+ c.parent.lastPos = c.pos
170170+ c.parent.pos++
171171+ }
172172+ for _, cg := range c.groups {
173173+ if n != nil {
174174+ n.AddComment(cg)
175175+ }
176176+ }
177177+ c.groups = nil
178178+ return n
179179+}
180180+181181+func (c *commentState) closeExpr(p *parser, n ast.Expr) ast.Expr {
182182+ c.closeNode(p, n)
183183+ return n
184184+}
185185+186186+func (c *commentState) closeClause(p *parser, n ast.Clause) ast.Clause {
187187+ c.closeNode(p, n)
188188+ return n
189189+}
190190+191191+// ----------------------------------------------------------------------------
192192+// Parsing support
193193+194194+func (p *parser) printTrace(a ...interface{}) {
195195+ const dots = ". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . "
196196+ const n = len(dots)
197197+ pos := p.file.Position(p.pos)
198198+ fmt.Printf("%5d:%3d: ", pos.Line, pos.Column)
199199+ i := 2 * p.indent
200200+ for i > n {
201201+ fmt.Print(dots)
202202+ i -= n
203203+ }
204204+ // i <= n
205205+ fmt.Print(dots[0:i])
206206+ fmt.Println(a...)
207207+}
208208+209209+func trace(p *parser, msg string) *parser {
210210+ p.printTrace(msg, "(")
211211+ p.indent++
212212+ return p
213213+}
214214+215215+// Usage pattern: defer un(trace(p, "..."))
216216+func un(p *parser) {
217217+ p.indent--
218218+ p.printTrace(")")
219219+}
220220+221221+// Advance to the next
222222+func (p *parser) next0() {
223223+ // Because of one-token look-ahead, print the previous token
224224+ // when tracing as it provides a more readable output. The
225225+ // very first token (!p.pos.IsValid()) is not initialized
226226+ // (it is ILLEGAL), so don't print it .
227227+ if p.trace && p.pos.IsValid() {
228228+ s := p.tok.String()
229229+ switch {
230230+ case p.tok.IsLiteral():
231231+ p.printTrace(s, p.lit)
232232+ case p.tok.IsOperator(), p.tok.IsKeyword():
233233+ p.printTrace("\"" + s + "\"")
234234+ default:
235235+ p.printTrace(s)
236236+ }
237237+ }
238238+239239+ p.pos, p.tok, p.lit = p.scanner.Scan()
240240+}
241241+242242+// Consume a comment and return it and the line on which it ends.
243243+func (p *parser) consumeComment() (comment *ast.Comment, endline int) {
244244+ // /*-style comments may end on a different line than where they start.
245245+ // Scan the comment for '\n' chars and adjust endline accordingly.
246246+ endline = p.file.Line(p.pos)
247247+ if p.lit[1] == '*' {
248248+ // don't use range here - no need to decode Unicode code points
249249+ for i := 0; i < len(p.lit); i++ {
250250+ if p.lit[i] == '\n' {
251251+ endline++
252252+ }
253253+ }
254254+ }
255255+256256+ comment = &ast.Comment{Slash: p.pos, Text: p.lit}
257257+ p.next0()
258258+259259+ return
260260+}
261261+262262+// Consume a group of adjacent comments, add it to the parser's
263263+// comments list, and return it together with the line at which
264264+// the last comment in the group ends. A non-comment token or n
265265+// empty lines terminate a comment group.
266266+func (p *parser) consumeCommentGroup(n int) (comments *ast.CommentGroup, endline int) {
267267+ var list []*ast.Comment
268268+ endline = p.file.Line(p.pos)
269269+ for p.tok == token.COMMENT && p.file.Line(p.pos) <= endline+n {
270270+ var comment *ast.Comment
271271+ comment, endline = p.consumeComment()
272272+ list = append(list, comment)
273273+ }
274274+275275+ cg := &ast.CommentGroup{List: list}
276276+ comments = cg
277277+ return
278278+}
279279+280280+// Advance to the next non-comment In the process, collect
281281+// any comment groups encountered, and refield the last lead and
282282+// and line comments.
283283+//
284284+// A lead comment is a comment group that starts and ends in a
285285+// line without any other tokens and that is followed by a non-comment
286286+// token on the line immediately after the comment group.
287287+//
288288+// A line comment is a comment group that follows a non-comment
289289+// token on the same line, and that has no tokens after it on the line
290290+// where it ends.
291291+//
292292+// Lead and line comments may be considered documentation that is
293293+// stored in the AST.
294294+func (p *parser) next() {
295295+ // A leadComment may not be consumed if it leads an inner token of a node.
296296+ if p.leadComment != nil {
297297+ p.comments.add(p.leadComment)
298298+ }
299299+ p.leadComment = nil
300300+ prev := p.pos
301301+ p.next0()
302302+ p.comments.pos++
303303+304304+ if p.tok == token.COMMENT {
305305+ var comment *ast.CommentGroup
306306+ var endline int
307307+308308+ if p.file.Line(p.pos) == p.file.Line(prev) {
309309+ // The comment is on same line as the previous token; it
310310+ // cannot be a lead comment but may be a line comment.
311311+ comment, endline = p.consumeCommentGroup(0)
312312+ if p.file.Line(p.pos) != endline {
313313+ // The next token is on a different line, thus
314314+ // the last comment group is a line comment.
315315+ comment.Line = true
316316+ }
317317+ }
318318+319319+ // consume successor comments, if any
320320+ endline = -1
321321+ for p.tok == token.COMMENT {
322322+ if comment != nil {
323323+ p.comments.add(comment)
324324+ }
325325+ comment, endline = p.consumeCommentGroup(1)
326326+ }
327327+328328+ if endline+1 == p.file.Line(p.pos) && p.tok != token.EOF {
329329+ // The next token is following on the line immediately after the
330330+ // comment group, thus the last comment group is a lead comment.
331331+ comment.Doc = true
332332+ p.leadComment = comment
333333+ } else {
334334+ p.comments.add(comment)
335335+ }
336336+ }
337337+}
338338+339339+// A bailout panic is raised to indicate early termination.
340340+type bailout struct{}
341341+342342+func (p *parser) error(pos token.Pos, msg string) {
343343+ ePos := p.file.Position(pos)
344344+345345+ // If AllErrors is not set, discard errors reported on the same line
346346+ // as the last recorded error and stop parsing if there are more than
347347+ // 10 errors.
348348+ if p.mode&allErrorsMode == 0 {
349349+ n := len(p.errors)
350350+ if n > 0 && p.errors[n-1].Position().Line == ePos.Line {
351351+ return // discard - likely a spurious error
352352+ }
353353+ if n > 10 {
354354+ panic(bailout{})
355355+ }
356356+ }
357357+358358+ p.errors.AddNew(ePos, msg)
359359+}
360360+361361+func (p *parser) errorExpected(pos token.Pos, msg string) {
362362+ msg = "expected " + msg
363363+ if pos == p.pos {
364364+ // the error happened at the current position;
365365+ // make the error message more specific
366366+ if p.tok == token.COMMA && p.lit == "\n" {
367367+ msg += ", found newline"
368368+ } else {
369369+ msg += ", found '" + p.tok.String() + "'"
370370+ if p.tok.IsLiteral() {
371371+ msg += " " + p.lit
372372+ }
373373+ }
374374+ }
375375+ p.error(pos, msg)
376376+}
377377+378378+func (p *parser) expect(tok token.Token) token.Pos {
379379+ pos := p.pos
380380+ if p.tok != tok {
381381+ p.errorExpected(pos, "'"+tok.String()+"'")
382382+ }
383383+ p.next() // make progress
384384+ return pos
385385+}
386386+387387+// expectClosing is like expect but provides a better error message
388388+// for the common case of a missing comma before a newline.
389389+func (p *parser) expectClosing(tok token.Token, context string) token.Pos {
390390+ if p.tok != tok && p.tok == token.COMMA && p.lit == "\n" {
391391+ p.error(p.pos, "missing ',' before newline in "+context)
392392+ p.next()
393393+ }
394394+ return p.expect(tok)
395395+}
396396+397397+func (p *parser) expectComma() {
398398+ // semicolon is optional before a closing ')', ']', '}', or newline
399399+ if p.tok != token.RPAREN && p.tok != token.RBRACE && p.tok != token.EOF {
400400+ switch p.tok {
401401+ case token.COMMA:
402402+ p.next()
403403+ default:
404404+ p.errorExpected(p.pos, "','")
405405+ syncExpr(p)
406406+ }
407407+ }
408408+}
409409+410410+func (p *parser) atComma(context string, follow ...token.Token) bool {
411411+ if p.tok == token.COMMA {
412412+ return true
413413+ }
414414+ for _, t := range follow {
415415+ if p.tok == t {
416416+ return false
417417+ }
418418+ }
419419+ msg := "missing ','"
420420+ // TODO: find a way to detect crossing lines now we don't have a semi.
421421+ if p.lit == "\n" {
422422+ msg += " before newline"
423423+ }
424424+ p.error(p.pos, msg+" in "+context)
425425+ return true // "insert" comma and continue
426426+}
427427+428428+func assert(cond bool, msg string) {
429429+ if !cond {
430430+ panic("lacelang/parser internal error: " + msg)
431431+ }
432432+}
433433+434434+// syncExpr advances to the next field in a field list.
435435+// Used for synchronization after an error.
436436+func syncExpr(p *parser) {
437437+ for {
438438+ switch p.tok {
439439+ case token.COMMA:
440440+ // Return only if parser made some progress since last
441441+ // sync or if it has not reached 10 sync calls without
442442+ // progress. Otherwise consume at least one token to
443443+ // avoid an endless parser loop (it is possible that
444444+ // both parseOperand and parseStmt call syncStmt and
445445+ // correctly do not advance, thus the need for the
446446+ // invocation limit p.syncCnt).
447447+ if p.pos == p.syncPos && p.syncCnt < 10 {
448448+ p.syncCnt++
449449+ return
450450+ }
451451+ if p.pos > p.syncPos {
452452+ p.syncPos = p.pos
453453+ p.syncCnt = 0
454454+ return
455455+ }
456456+ // Reaching here indicates a parser bug, likely an
457457+ // incorrect token list in this function, but it only
458458+ // leads to skipping of possibly correct code if a
459459+ // previous error is present, and thus is preferred
460460+ // over a non-terminating parse.
461461+ case token.EOF:
462462+ return
463463+ }
464464+ p.next()
465465+ }
466466+}
467467+468468+// safePos returns a valid file position for a given position: If pos
469469+// is valid to begin with, safePos returns pos. If pos is out-of-range,
470470+// safePos returns the EOF position.
471471+//
472472+// This is hack to work around "artificial" end positions in the AST which
473473+// are computed by adding 1 to (presumably valid) token positions. If the
474474+// token positions are invalid due to parse errors, the resulting end position
475475+// may be past the file's EOF position, which would lead to panics if used
476476+// later on.
477477+func (p *parser) safePos(pos token.Pos) (res token.Pos) {
478478+ defer func() {
479479+ if recover() != nil {
480480+ res = token.Pos(p.file.Base() + p.file.Size()) // EOF position
481481+ }
482482+ }()
483483+ _ = p.file.Offset(pos) // trigger a panic if position is out-of-range
484484+ return pos
485485+}
486486+487487+// ----------------------------------------------------------------------------
488488+// Identifiers
489489+490490+func (p *parser) parseIdent() *ast.Ident {
491491+ c := p.openComments()
492492+ pos := p.pos
493493+ name := "_"
494494+ if p.tok == token.IDENT {
495495+ name = p.lit
496496+ p.next()
497497+ } else {
498498+ p.expect(token.IDENT) // use expect() error handling
499499+ }
500500+ ident := &ast.Ident{NamePos: pos, Name: name}
501501+ c.closeNode(p, ident)
502502+ return ident
503503+}
504504+505505+// ----------------------------------------------------------------------------
506506+// Expressions
507507+508508+// parseOperand returns an expression.
509509+// Callers must verify the result.
510510+func (p *parser) parseOperand() (expr ast.Expr) {
511511+ if p.trace {
512512+ defer un(trace(p, "Operand"))
513513+ }
514514+515515+ switch p.tok {
516516+ case token.IDENT:
517517+ return p.parseIdent()
518518+519519+ case token.LBRACE:
520520+ return p.parseStruct()
521521+522522+ case token.LBRACK:
523523+ return p.parseList()
524524+525525+ case token.BOTTOM:
526526+ c := p.openComments()
527527+ x := &ast.BottomLit{Bottom: p.pos}
528528+ p.next()
529529+ return c.closeExpr(p, x)
530530+531531+ case token.NULL, token.TRUE, token.FALSE, token.INT, token.FLOAT, token.STRING:
532532+ c := p.openComments()
533533+ x := &ast.BasicLit{ValuePos: p.pos, Kind: p.tok, Value: p.lit}
534534+ p.next()
535535+ return c.closeExpr(p, x)
536536+537537+ case token.INTERPOLATION:
538538+ return p.parseInterpolation()
539539+540540+ case token.LPAREN:
541541+ c := p.openComments()
542542+ defer func() { c.closeNode(p, expr) }()
543543+ lparen := p.pos
544544+ p.next()
545545+ if p.tok == token.RPAREN && p.mode&parseLambdasMode != 0 {
546546+ c.pos = 2
547547+ rparen := p.expect(token.RPAREN)
548548+ p.expect(token.LAMBDA)
549549+ return &ast.LambdaExpr{
550550+ Lparen: lparen,
551551+ Rparen: rparen,
552552+ Expr: p.parseRHS(),
553553+ }
554554+ }
555555+ p.exprLev++
556556+ p.openList()
557557+ x := p.parseRHS() // types may be parenthesized: (some type)
558558+ var params []*ast.Field
559559+ ident, ok := x.(*ast.Ident)
560560+ if ok && (p.tok == token.COLON || p.tok == token.COMMA) && p.mode&parseLambdasMode != 0 {
561561+ params = p.parseParams(ident, token.RPAREN)
562562+ }
563563+ p.closeList()
564564+ p.exprLev--
565565+ rparen := p.expect(token.RPAREN)
566566+ if p.tok == token.LAMBDA || params != nil && p.mode&parseLambdasMode != 0 {
567567+ p.expect(token.LAMBDA)
568568+ if params == nil {
569569+ m := &ast.Field{Label: ident}
570570+ params = append(params, m)
571571+ }
572572+ return &ast.LambdaExpr{
573573+ Lparen: lparen,
574574+ Params: params,
575575+ Rparen: rparen,
576576+ Expr: p.parseRHS(),
577577+ }
578578+ }
579579+ return &ast.ParenExpr{
580580+ Lparen: lparen,
581581+ X: x,
582582+ Rparen: rparen}
583583+ }
584584+585585+ // we have an error
586586+ c := p.openComments()
587587+ pos := p.pos
588588+ p.errorExpected(pos, "operand")
589589+ syncExpr(p)
590590+ return c.closeExpr(p, &ast.BadExpr{From: pos, To: p.pos})
591591+}
592592+593593+func (p *parser) parseParams(ident *ast.Ident, follow token.Token) (params []*ast.Field) {
594594+ for {
595595+ c := p.openComments()
596596+ if ident == nil {
597597+ ident = p.parseIdent()
598598+ }
599599+ m := &ast.Field{Label: ident}
600600+ if p.tok == token.COLON {
601601+ m.Colon = p.expect(token.COLON)
602602+ m.Value = p.parseRHS()
603603+ }
604604+ hasComma := p.tok == token.COMMA
605605+ if hasComma {
606606+ p.expect(token.COMMA)
607607+ }
608608+ c.closeNode(p, m)
609609+ params = append(params, m)
610610+ if !hasComma || p.tok == follow || p.tok == token.EOF {
611611+ break
612612+ }
613613+ ident = nil
614614+ }
615615+ return params
616616+}
617617+618618+func (p *parser) parseIndexOrSlice(x ast.Expr) (expr ast.Expr) {
619619+ if p.trace {
620620+ defer un(trace(p, "IndexOrSlice"))
621621+ }
622622+623623+ c := p.openComments()
624624+ defer func() { c.closeNode(p, expr) }()
625625+ c.pos = 1
626626+627627+ const N = 2
628628+ lbrack := p.expect(token.LBRACK)
629629+630630+ p.exprLev++
631631+ var index [N]ast.Expr
632632+ var colons [N - 1]token.Pos
633633+ if p.tok != token.COLON {
634634+ index[0] = p.parseRHS()
635635+ }
636636+ nColons := 0
637637+ for p.tok == token.COLON && nColons < len(colons) {
638638+ colons[nColons] = p.pos
639639+ nColons++
640640+ p.next()
641641+ if p.tok != token.COLON && p.tok != token.RBRACK && p.tok != token.EOF {
642642+ index[nColons] = p.parseRHS()
643643+ }
644644+ }
645645+ p.exprLev--
646646+ rbrack := p.expect(token.RBRACK)
647647+648648+ if nColons > 0 {
649649+ return &ast.SliceExpr{
650650+ X: x,
651651+ Lbrack: lbrack,
652652+ Low: index[0],
653653+ High: index[1],
654654+ Rbrack: rbrack}
655655+ }
656656+657657+ return &ast.IndexExpr{
658658+ X: x,
659659+ Lbrack: lbrack,
660660+ Index: index[0],
661661+ Rbrack: rbrack}
662662+}
663663+664664+func (p *parser) parseCallOrConversion(fun ast.Expr) (expr *ast.CallExpr) {
665665+ if p.trace {
666666+ defer un(trace(p, "CallOrConversion"))
667667+ }
668668+ c := p.openComments()
669669+ defer func() { c.closeNode(p, expr) }()
670670+671671+ lparen := p.expect(token.LPAREN)
672672+ p.exprLev++
673673+ var list []ast.Expr
674674+ for p.tok != token.RPAREN && p.tok != token.EOF {
675675+ list = append(list, p.parseRHS()) // builtins may expect a type: make(some type, ...)
676676+ if !p.atComma("argument list", token.RPAREN) {
677677+ break
678678+ }
679679+ p.next()
680680+ }
681681+ p.exprLev--
682682+ rparen := p.expectClosing(token.RPAREN, "argument list")
683683+684684+ return &ast.CallExpr{
685685+ Fun: fun,
686686+ Lparen: lparen,
687687+ Args: list,
688688+ Rparen: rparen}
689689+}
690690+691691+func (p *parser) parseFieldList(allowEmit bool) (list []ast.Decl) {
692692+ if p.trace {
693693+ defer un(trace(p, "FieldList"))
694694+ }
695695+ origEmit := allowEmit
696696+ p.openList()
697697+ defer p.closeList()
698698+699699+ for p.tok != token.RBRACE && p.tok != token.EOF {
700700+ d := p.parseField(allowEmit)
701701+ if e, ok := d.(*ast.EmitDecl); ok {
702702+ if origEmit && !allowEmit {
703703+ p.error(p.pos, "only one emit allowed at top level")
704704+ }
705705+ if !origEmit || !allowEmit {
706706+ d = &ast.BadDecl{From: e.Pos(), To: e.End()}
707707+ for _, cg := range e.Comments() {
708708+ d.AddComment(cg)
709709+ }
710710+ }
711711+ // uncomment to only allow one emit per top-level
712712+ // allowEmit = false
713713+ }
714714+ list = append(list, d)
715715+ }
716716+ return
717717+}
718718+func (p *parser) parseField(allowEmit bool) (decl ast.Decl) {
719719+ if p.trace {
720720+ defer un(trace(p, "Field"))
721721+ }
722722+723723+ c := p.openComments()
724724+ defer func() { c.closeNode(p, decl) }()
725725+726726+ pos := p.pos
727727+728728+ this := &ast.Field{Label: nil}
729729+ m := this
730730+731731+ for i := 0; ; i++ {
732732+ tok := p.tok
733733+734734+ expr, ok := p.parseLabel(m)
735735+736736+ if !ok {
737737+ if !allowEmit {
738738+ p.error(pos, "expected label, found "+tok.String())
739739+ }
740740+ if expr == nil {
741741+ expr = p.parseExpr()
742742+ }
743743+ e := &ast.EmitDecl{Expr: expr}
744744+ if p.atComma("file", token.RBRACE) {
745745+ p.next()
746746+ }
747747+ return e
748748+ }
749749+750750+ if i == 0 && tok == token.IDENT {
751751+ ident := expr.(*ast.Ident)
752752+ switch p.tok {
753753+ case token.BIND:
754754+ pos := p.pos
755755+ p.expect(token.BIND)
756756+ ref := p.parseRHS()
757757+ if p.atComma("struct literal", token.RBRACE) { // TODO: may be EOF
758758+ p.next()
759759+ }
760760+ return &ast.Alias{Ident: ident, Equal: pos, Expr: ref}
761761+762762+ case token.LPAREN:
763763+ var value ast.Expr
764764+ if p.mode&parseLambdasMode != 0 {
765765+ c.pos = 2
766766+ // TODO: Only allow LambdaExpr after non-quoted identifier.
767767+ value = p.parseOperand()
768768+ if _, ok := unparen(value).(*ast.LambdaExpr); !ok {
769769+ p.error(value.Pos(), "expected lambda expression")
770770+ }
771771+ }
772772+ if p.atComma("struct literal", token.RBRACE) { // TODO: may be EOF
773773+ p.next()
774774+ }
775775+ return &ast.Field{Label: ident, Value: value}
776776+ }
777777+ }
778778+779779+ if p.tok == token.COLON {
780780+ break
781781+ }
782782+783783+ switch p.tok {
784784+ default:
785785+ if !allowEmit || p.tok != token.COMMA {
786786+ p.errorExpected(p.pos, "label or ':'")
787787+ }
788788+ switch tok {
789789+ case token.IDENT, token.LBRACK, token.STRING, token.INTERPOLATION, token.NULL, token.TRUE, token.FALSE:
790790+ if p.tok == token.COMMA {
791791+ p.expectComma()
792792+ return &ast.EmitDecl{Expr: expr}
793793+ }
794794+ }
795795+ return &ast.BadDecl{From: pos, To: p.pos}
796796+797797+ case token.IDENT, token.STRING, token.LSS, token.INTERPOLATION, token.LBRACK:
798798+ field := &ast.Field{}
799799+ m.Value = &ast.StructLit{Elts: []ast.Decl{field}}
800800+ m = field
801801+ }
802802+803803+ allowEmit = false
804804+ }
805805+806806+ this.Colon = p.pos
807807+ p.expect(token.COLON)
808808+ m.Value = p.parseRHS()
809809+810810+ decl = this
811811+ var arrow token.Pos
812812+ switch p.tok {
813813+ case token.ARROW:
814814+ arrow = p.expect(token.ARROW)
815815+ fallthrough
816816+817817+ case token.FOR, token.IF:
818818+ clauses := p.parseComprehensionClauses()
819819+ return &ast.ComprehensionDecl{
820820+ Field: this,
821821+ Select: arrow,
822822+ Clauses: clauses,
823823+ }
824824+ }
825825+826826+ if p.atComma("struct literal", token.RBRACE) { // TODO: may be EOF
827827+ p.next()
828828+ }
829829+830830+ return decl
831831+}
832832+833833+func (p *parser) parseLabel(f *ast.Field) (expr ast.Expr, ok bool) {
834834+ switch p.tok {
835835+ case token.IDENT:
836836+ ident := p.parseIdent()
837837+ f.Label = ident
838838+ expr = ident
839839+840840+ case token.STRING:
841841+ // JSON compatibility.
842842+843843+ expr = p.parseOperand()
844844+ f.Label = expr.(ast.Label)
845845+846846+ case token.INTERPOLATION:
847847+ expr = p.parseInterpolation()
848848+ f.Label = expr.(ast.Label)
849849+850850+ case token.NULL, token.TRUE, token.FALSE:
851851+ // Keywords that represent operands.
852852+853853+ // Allowing keywords to be used as a labels should not interfere with
854854+ // generating good errors: any keyword can only appear on the RHS of a
855855+ // field (after a ':'), whereas labels always appear on the LHS.
856856+ ident := &ast.BasicLit{
857857+ Kind: p.tok,
858858+ ValuePos: p.pos,
859859+ Value: p.lit,
860860+ }
861861+ p.next()
862862+ f.Label = ident
863863+ expr = ident
864864+865865+ case token.IF, token.FOR, token.IN, token.LET:
866866+ // Keywords representing clauses.
867867+ f.Label = &ast.Ident{
868868+ NamePos: p.pos,
869869+ Name: p.lit,
870870+ }
871871+ p.next()
872872+873873+ case token.LSS: // element templates
874874+ pos := p.pos
875875+ c := p.openComments()
876876+ p.next()
877877+ ident := p.parseIdent()
878878+ gtr := p.pos
879879+ if p.tok != token.GTR {
880880+ p.expect(token.GTR)
881881+ }
882882+ p.next()
883883+ label := &ast.TemplateLabel{Langle: pos, Ident: ident, Rangle: gtr}
884884+ c.closeNode(p, label)
885885+ f.Label = label
886886+887887+ case token.LBRACK:
888888+ expr = p.parseList()
889889+ list, ok := expr.(*ast.ListLit)
890890+ if ok && len(list.Elts) == 1 && list.Ellipsis == token.NoPos {
891891+ f.Label = &ast.ExprLabel{
892892+ Lbrack: list.Lbrack,
893893+ Label: list.Elts[0],
894894+ Rbrack: list.Rbrack,
895895+ }
896896+ break
897897+ }
898898+899899+ fallthrough
900900+ default:
901901+ return expr, false
902902+ }
903903+ return expr, true
904904+}
905905+906906+func (p *parser) parseStruct() (expr ast.Expr) {
907907+ c := p.openComments()
908908+ defer func() { c.closeNode(p, expr) }()
909909+910910+ lbrace := p.expect(token.LBRACE)
911911+912912+ if p.trace {
913913+ defer un(trace(p, "StructLit"))
914914+ }
915915+916916+ elts := p.parseStructBody()
917917+ rbrace := p.expectClosing(token.RBRACE, "struct literal")
918918+ return &ast.StructLit{
919919+ Lbrace: lbrace,
920920+ Elts: elts,
921921+ Rbrace: rbrace,
922922+ }
923923+}
924924+925925+func (p *parser) parseStructBody() []ast.Decl {
926926+ if p.trace {
927927+ defer un(trace(p, "StructBody"))
928928+ }
929929+930930+ p.exprLev++
931931+ var elts []ast.Decl
932932+ if p.tok != token.RBRACE {
933933+ elts = p.parseFieldList(false)
934934+ }
935935+ p.exprLev--
936936+937937+ return elts
938938+}
939939+940940+func isClauseStart(tok token.Token) bool {
941941+ return tok == token.FOR || tok == token.IF // || tok == LET
942942+}
943943+944944+func (p *parser) parseComprehensionClauses() (clauses []ast.Clause) {
945945+ // TODO: reuse Template spec, which is possible if it doesn't check the
946946+ // first is an identifier.
947947+ for {
948948+ if p.tok == token.COMMA {
949949+ p.next()
950950+ }
951951+ switch p.tok {
952952+ case token.FOR:
953953+ c := p.openComments()
954954+ forPos := p.expect(token.FOR)
955955+ var key, value *ast.Ident
956956+ var colon token.Pos
957957+ value = p.parseIdent()
958958+ if p.tok == token.COMMA {
959959+ colon = p.expect(token.COMMA)
960960+ key = value
961961+ value = p.parseIdent()
962962+ }
963963+ c.pos = 4
964964+ // params := p.parseParams(nil, ARROW)
965965+ clauses = append(clauses, c.closeClause(p, &ast.ForClause{
966966+ For: forPos,
967967+ Key: key,
968968+ Colon: colon,
969969+ Value: value,
970970+ In: p.expect(token.IN),
971971+ Source: p.parseExpr(),
972972+ }))
973973+974974+ case token.IF:
975975+ c := p.openComments()
976976+ clauses = append(clauses, c.closeClause(p, &ast.IfClause{
977977+ If: p.expect(token.IF),
978978+ Condition: p.parseExpr(),
979979+ }))
980980+981981+ // TODO: case LET:
982982+ default:
983983+ return clauses
984984+ }
985985+ }
986986+}
987987+988988+func (p *parser) parseList() (expr ast.Expr) {
989989+ c := p.openComments()
990990+ defer func() { c.closeNode(p, expr) }()
991991+992992+ lbrack := p.expect(token.LBRACK)
993993+994994+ if p.trace {
995995+ defer un(trace(p, "ListLiteral"))
996996+ }
997997+998998+ elts := p.parseListElements()
999999+10001000+ if clauses := p.parseComprehensionClauses(); clauses != nil {
10011001+ var expr ast.Expr
10021002+ if len(elts) != 1 {
10031003+ p.error(lbrack+1, "list comprehension must have exactly one element")
10041004+ }
10051005+ if len(elts) > 0 {
10061006+ expr = elts[0]
10071007+ }
10081008+ rbrack := p.expectClosing(token.RBRACK, "list comprehension")
10091009+10101010+ return &ast.ListComprehension{
10111011+ Lbrack: lbrack,
10121012+ Expr: expr,
10131013+ Clauses: clauses,
10141014+ Rbrack: rbrack,
10151015+ }
10161016+ }
10171017+10181018+ ellipsis := token.NoPos
10191019+ typ := ast.Expr(nil)
10201020+ if p.tok == token.ELLIPSIS {
10211021+ ellipsis = p.pos
10221022+ p.next()
10231023+ if p.tok != token.COMMA && p.tok != token.RBRACK {
10241024+ typ = p.parseRHS()
10251025+ }
10261026+ if p.atComma("list literal", token.RBRACK) {
10271027+ p.next()
10281028+ }
10291029+ }
10301030+10311031+ rbrack := p.expectClosing(token.RBRACK, "list literal")
10321032+ return &ast.ListLit{
10331033+ Lbrack: lbrack,
10341034+ Elts: elts,
10351035+ Ellipsis: ellipsis,
10361036+ Type: typ,
10371037+ Rbrack: rbrack}
10381038+}
10391039+10401040+func (p *parser) parseListElements() (list []ast.Expr) {
10411041+ if p.trace {
10421042+ defer un(trace(p, "ListElements"))
10431043+ }
10441044+ p.openList()
10451045+ defer p.closeList()
10461046+10471047+ for p.tok != token.RBRACK && p.tok != token.ELLIPSIS && p.tok != token.EOF {
10481048+ list = append(list, p.parseListElement())
10491049+ // Enforce there is an explicit comma. We could also allow the
10501050+ // omission of commas in lists, but this gives rise to some ambiguities
10511051+ // with list comprehensions.
10521052+ if p.tok == token.COMMA && p.lit != "," {
10531053+ p.next()
10541054+ // Allow missing comma for last element, though, to be compliant
10551055+ // with JSON.
10561056+ if p.tok == token.RBRACK || p.tok == token.FOR || p.tok == token.IF {
10571057+ break
10581058+ }
10591059+ p.error(p.pos, "missing ',' before newline in list literal")
10601060+ } else if !p.atComma("list literal", token.RBRACK, token.FOR, token.IF) {
10611061+ break
10621062+ }
10631063+ p.next()
10641064+ }
10651065+10661066+ return
10671067+}
10681068+10691069+func (p *parser) parseListElement() (expr ast.Expr) {
10701070+ if p.trace {
10711071+ defer un(trace(p, "ListElement"))
10721072+ }
10731073+ c := p.openComments()
10741074+ defer func() { c.closeNode(p, expr) }()
10751075+10761076+ e := p.parseRHS()
10771077+ switch p.tok {
10781078+ case token.ELLIPSIS:
10791079+ return &ast.Ellipsis{Ellipsis: p.expect(token.ELLIPSIS), Elt: e}
10801080+ }
10811081+ return e
10821082+}
10831083+10841084+// checkExpr checks that x is an expression (and not a type).
10851085+func (p *parser) checkExpr(x ast.Expr) ast.Expr {
10861086+ switch unparen(x).(type) {
10871087+ case *ast.BadExpr:
10881088+ case *ast.BottomLit:
10891089+ case *ast.Ident:
10901090+ case *ast.BasicLit:
10911091+ case *ast.Interpolation:
10921092+ case *ast.StructLit:
10931093+ case *ast.ListLit:
10941094+ case *ast.LambdaExpr:
10951095+ case *ast.ListComprehension:
10961096+ case *ast.ParenExpr:
10971097+ panic("unreachable")
10981098+ case *ast.SelectorExpr:
10991099+ case *ast.IndexExpr:
11001100+ case *ast.SliceExpr:
11011101+ case *ast.CallExpr:
11021102+ case *ast.UnaryExpr:
11031103+ case *ast.BinaryExpr:
11041104+ default:
11051105+ // all other nodes are not proper expressions
11061106+ p.errorExpected(x.Pos(), "expression")
11071107+ x = &ast.BadExpr{
11081108+ From: x.Pos(), To: p.safePos(x.End()),
11091109+ }
11101110+ }
11111111+ return x
11121112+}
11131113+11141114+// If x is of the form (T), unparen returns unparen(T), otherwise it returns x.
11151115+func unparen(x ast.Expr) ast.Expr {
11161116+ if p, isParen := x.(*ast.ParenExpr); isParen {
11171117+ x = unparen(p.X)
11181118+ }
11191119+ return x
11201120+}
11211121+11221122+// If lhs is set and the result is an identifier, it is not resolved.
11231123+func (p *parser) parsePrimaryExpr() ast.Expr {
11241124+ if p.trace {
11251125+ defer un(trace(p, "PrimaryExpr"))
11261126+ }
11271127+11281128+ x := p.parseOperand()
11291129+11301130+L:
11311131+ for {
11321132+ switch p.tok {
11331133+ case token.PERIOD:
11341134+ c := p.openComments()
11351135+ c.pos = 1
11361136+ p.next()
11371137+ switch p.tok {
11381138+ case token.IDENT:
11391139+ x = &ast.SelectorExpr{
11401140+ X: p.checkExpr(x),
11411141+ Sel: p.parseIdent(),
11421142+ }
11431143+ default:
11441144+ pos := p.pos
11451145+ p.errorExpected(pos, "selector")
11461146+ p.next() // make progress
11471147+ x = &ast.SelectorExpr{X: x, Sel: &ast.Ident{NamePos: pos, Name: "_"}}
11481148+ }
11491149+ c.closeNode(p, x)
11501150+ case token.LBRACK:
11511151+ x = p.parseIndexOrSlice(p.checkExpr(x))
11521152+ case token.LPAREN:
11531153+ x = p.parseCallOrConversion(p.checkExpr(x))
11541154+ default:
11551155+ break L
11561156+ }
11571157+ }
11581158+11591159+ return x
11601160+}
11611161+11621162+// If lhs is set and the result is an identifier, it is not resolved.
11631163+func (p *parser) parseUnaryExpr() ast.Expr {
11641164+ if p.trace {
11651165+ defer un(trace(p, "UnaryExpr"))
11661166+ }
11671167+11681168+ switch p.tok {
11691169+ case token.ADD, token.SUB, token.NOT:
11701170+ pos, op := p.pos, p.tok
11711171+ c := p.openComments()
11721172+ p.next()
11731173+ return c.closeExpr(p, &ast.UnaryExpr{
11741174+ OpPos: pos,
11751175+ Op: op,
11761176+ X: p.checkExpr(p.parseUnaryExpr()),
11771177+ })
11781178+ }
11791179+11801180+ return p.parsePrimaryExpr()
11811181+}
11821182+11831183+func (p *parser) tokPrec() (token.Token, int) {
11841184+ tok := p.tok
11851185+ if tok == token.IDENT {
11861186+ switch p.lit {
11871187+ case "quo":
11881188+ return token.IQUO, 7
11891189+ case "rem":
11901190+ return token.IREM, 7
11911191+ case "div":
11921192+ return token.IDIV, 7
11931193+ case "mod":
11941194+ return token.IMOD, 7
11951195+ default:
11961196+ return tok, 0
11971197+ }
11981198+ }
11991199+ return tok, tok.Precedence()
12001200+}
12011201+12021202+// If lhs is set and the result is an identifier, it is not resolved.
12031203+func (p *parser) parseBinaryExpr(prec1 int) ast.Expr {
12041204+ if p.trace {
12051205+ defer un(trace(p, "BinaryExpr"))
12061206+ }
12071207+ p.openList()
12081208+ defer p.closeList()
12091209+12101210+ x := p.parseUnaryExpr()
12111211+12121212+ for {
12131213+ op, prec := p.tokPrec()
12141214+ if prec < prec1 {
12151215+ return x
12161216+ }
12171217+ c := p.openComments()
12181218+ c.pos = 1
12191219+ pos := p.expect(p.tok)
12201220+ x = c.closeExpr(p, &ast.BinaryExpr{
12211221+ X: p.checkExpr(x),
12221222+ OpPos: pos,
12231223+ Op: op,
12241224+ Y: p.checkExpr(p.parseBinaryExpr(prec + 1))})
12251225+ }
12261226+}
12271227+12281228+func (p *parser) parseInterpolation() (expr ast.Expr) {
12291229+ c := p.openComments()
12301230+ defer func() { c.closeNode(p, expr) }()
12311231+12321232+ p.openList()
12331233+ defer p.closeList()
12341234+12351235+ cc := p.openComments()
12361236+12371237+ lit := p.lit
12381238+ p.next()
12391239+ last := &ast.BasicLit{ValuePos: p.pos, Kind: token.STRING, Value: lit}
12401240+ exprs := []ast.Expr{last}
12411241+12421242+ quote := rune(lit[0])
12431243+ numQuotes := 1
12441244+ if len(lit) > 2 && lit[0] == lit[1] {
12451245+ numQuotes = 3
12461246+ }
12471247+12481248+ for p.tok == token.LPAREN {
12491249+ c.pos = 1
12501250+ p.expect(token.LPAREN)
12511251+ cc.closeExpr(p, last)
12521252+12531253+ exprs = append(exprs, p.parseExpr())
12541254+12551255+ cc = p.openComments()
12561256+ if p.tok != token.RPAREN {
12571257+ p.error(p.pos, "expected ')' for string interpolation")
12581258+ }
12591259+ lit = p.scanner.ResumeInterpolation(quote, numQuotes)
12601260+ p.next()
12611261+ last = &ast.BasicLit{
12621262+ ValuePos: p.pos,
12631263+ Kind: token.STRING,
12641264+ Value: lit,
12651265+ }
12661266+ exprs = append(exprs, last)
12671267+ }
12681268+ cc.closeExpr(p, last)
12691269+ return &ast.Interpolation{Elts: exprs}
12701270+}
12711271+12721272+// Callers must check the result (using checkExpr), depending on context.
12731273+func (p *parser) parseExpr() ast.Expr {
12741274+ if p.trace {
12751275+ defer un(trace(p, "Expression"))
12761276+ }
12771277+12781278+ return p.parseBinaryExpr(token.LowestPrec + 1)
12791279+}
12801280+12811281+func (p *parser) parseRHS() ast.Expr {
12821282+ x := p.checkExpr(p.parseExpr())
12831283+ return x
12841284+}
12851285+12861286+func (p *parser) parseCallExpr(callType string) *ast.CallExpr {
12871287+ x := p.parseRHS() // could be a conversion: (some type)(x)
12881288+ if call, isCall := x.(*ast.CallExpr); isCall {
12891289+ return call
12901290+ }
12911291+ if _, isBad := x.(*ast.BadExpr); !isBad {
12921292+ // only report error if it's a new one
12931293+ p.error(p.safePos(x.End()), fmt.Sprintf("function must be invoked in %s statement", callType))
12941294+ }
12951295+ return nil
12961296+}
12971297+12981298+// ----------------------------------------------------------------------------
12991299+// Declarations
13001300+13011301+type parseSpecFunction func(iota int) *ast.ImportSpec
13021302+13031303+func isValidImport(lit string) bool {
13041304+ const illegalChars = `!"#$%&'()*,:;<=>?[\]^{|}` + "`\uFFFD"
13051305+ s, _ := strconv.Unquote(lit) // go/scanner returns a legal string literal
13061306+ for _, r := range s {
13071307+ if !unicode.IsGraphic(r) || unicode.IsSpace(r) || strings.ContainsRune(illegalChars, r) {
13081308+ return false
13091309+ }
13101310+ }
13111311+ return s != ""
13121312+}
13131313+13141314+func (p *parser) parseImportSpec(_ int) *ast.ImportSpec {
13151315+ if p.trace {
13161316+ defer un(trace(p, "ImportSpec"))
13171317+ }
13181318+13191319+ c := p.openComments()
13201320+13211321+ var ident *ast.Ident
13221322+ switch p.tok {
13231323+ case token.PERIOD:
13241324+ ident = &ast.Ident{NamePos: p.pos, Name: "."}
13251325+ p.next()
13261326+ case token.IDENT:
13271327+ ident = p.parseIdent()
13281328+ }
13291329+13301330+ pos := p.pos
13311331+ var path string
13321332+ if p.tok == token.STRING {
13331333+ path = p.lit
13341334+ if !isValidImport(path) {
13351335+ p.error(pos, "invalid import path: "+path)
13361336+ }
13371337+ p.next()
13381338+ p.expectComma() // call before accessing p.linecomment
13391339+ } else {
13401340+ p.expect(token.STRING) // use expect() error handling
13411341+ if p.tok == token.COMMA {
13421342+ p.expectComma() // call before accessing p.linecomment
13431343+ }
13441344+ }
13451345+ // collect imports
13461346+ spec := &ast.ImportSpec{
13471347+ Name: ident,
13481348+ Path: &ast.BasicLit{ValuePos: pos, Kind: token.STRING, Value: path},
13491349+ }
13501350+ c.closeNode(p, spec)
13511351+ p.imports = append(p.imports, spec)
13521352+13531353+ return spec
13541354+}
13551355+13561356+func (p *parser) parseImports() *ast.ImportDecl {
13571357+ if p.trace {
13581358+ defer un(trace(p, "Imports"))
13591359+ }
13601360+ c := p.openComments()
13611361+13621362+ ident := p.parseIdent()
13631363+ var lparen, rparen token.Pos
13641364+ var list []*ast.ImportSpec
13651365+ if p.tok == token.LPAREN {
13661366+ lparen = p.pos
13671367+ p.next()
13681368+ p.openList()
13691369+ for iota := 0; p.tok != token.RPAREN && p.tok != token.EOF; iota++ {
13701370+ list = append(list, p.parseImportSpec(iota))
13711371+ }
13721372+ p.closeList()
13731373+ rparen = p.expect(token.RPAREN)
13741374+ p.expectComma()
13751375+ } else {
13761376+ list = append(list, p.parseImportSpec(0))
13771377+ }
13781378+13791379+ d := &ast.ImportDecl{
13801380+ Import: ident.Pos(),
13811381+ Lparen: lparen,
13821382+ Specs: list,
13831383+ Rparen: rparen,
13841384+ }
13851385+ c.closeNode(p, d)
13861386+ return d
13871387+}
13881388+13891389+// ----------------------------------------------------------------------------
13901390+// Source files
13911391+13921392+func (p *parser) parseFile() *ast.File {
13931393+ if p.trace {
13941394+ defer un(trace(p, "File"))
13951395+ }
13961396+13971397+ c := p.comments
13981398+13991399+ // Don't bother parsing the rest if we had errors scanning the first
14001400+ // Likely not a Go source file at all.
14011401+ if p.errors.Len() != 0 {
14021402+ return nil
14031403+ }
14041404+14051405+ // The package clause is not a declaration: it does not appear in any
14061406+ // scope.
14071407+ pos := p.pos
14081408+ var name *ast.Ident
14091409+ if p.tok == token.IDENT && p.lit == "package" {
14101410+ p.expect(token.IDENT)
14111411+ name = p.parseIdent()
14121412+ if name.Name == "_" && p.mode&declarationErrorsMode != 0 {
14131413+ p.error(p.pos, "invalid package name _")
14141414+ }
14151415+ p.expectComma()
14161416+ } else {
14171417+ pos = token.NoPos
14181418+ }
14191419+ c.pos = 3
14201420+14211421+ p.openList()
14221422+ var decls []ast.Decl
14231423+ if p.mode&packageClauseOnlyMode == 0 {
14241424+ // import decls
14251425+ for p.tok == token.IDENT && p.lit == "import" {
14261426+ decls = append(decls, p.parseImports())
14271427+ }
14281428+14291429+ if p.mode&importsOnlyMode == 0 {
14301430+ // rest of package decls
14311431+ // TODO: loop and allow multiple expressions.
14321432+ decls = append(decls, p.parseFieldList(true)...)
14331433+ p.expect(token.EOF)
14341434+ }
14351435+ }
14361436+ p.closeList()
14371437+14381438+ f := &ast.File{
14391439+ Package: pos,
14401440+ Name: name,
14411441+ Imports: p.imports,
14421442+ Decls: decls,
14431443+ }
14441444+ c.closeNode(p, f)
14451445+ return f
14461446+}
+543
cue/parser/parser_test.go
···11+// Copyright 2018 The CUE Authors
22+//
33+// Licensed under the Apache License, Version 2.0 (the "License");
44+// you may not use this file except in compliance with the License.
55+// You may obtain a copy of the License at
66+//
77+// http://www.apache.org/licenses/LICENSE-2.0
88+//
99+// Unless required by applicable law or agreed to in writing, software
1010+// distributed under the License is distributed on an "AS IS" BASIS,
1111+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1212+// See the License for the specific language governing permissions and
1313+// limitations under the License.
1414+1515+package parser
1616+1717+import (
1818+ "bytes"
1919+ "fmt"
2020+ "strings"
2121+ "testing"
2222+2323+ "cuelang.org/go/cue/ast"
2424+ "cuelang.org/go/cue/token"
2525+)
2626+2727+func TestParse(t *testing.T) {
2828+ testCases := []struct{ desc, in, out string }{{
2929+ "empty file", "", "",
3030+ }, {
3131+ "empty struct", "{}", "{}",
3232+ }, {
3333+ "empty structs", "{},{},", "{}, {}",
3434+ }, {
3535+ "empty structs; elided comma", "{}\n{}", "{}, {}",
3636+ }, {
3737+ "basic lits", `"a","b", 3,3.4,5,2_3`, `"a", "b", 3, 3.4, 5, 2_3`,
3838+ }, {
3939+ "keyword basic lits", `true,false,null`, `true, false, null`,
4040+ }, {
4141+ "keywords as labels",
4242+ `if: 0, for: 1, in: 2, where: 3, div: 4, quo: 5`,
4343+ `if: 0, for: 1, in: 2, where: 3, div: 4, quo: 5`,
4444+ }, {
4545+ "json",
4646+ `{
4747+ "a": 1,
4848+ "b": "2",
4949+ "c": 3
5050+ }`,
5151+ `{"a": 1, "b": "2", "c": 3}`,
5252+ }, {
5353+ "json:extra comma",
5454+ `{
5555+ "a": 1,
5656+ "b": "2",
5757+ "c": 3,
5858+ }`,
5959+ `{"a": 1, "b": "2", "c": 3}`,
6060+ }, {
6161+ "json:simplified",
6262+ `{
6363+ a: 1
6464+ b: "2"
6565+ c: 3
6666+ }`,
6767+ `{a: 1, b: "2", c: 3}`,
6868+ }, {
6969+ "not emitted",
7070+ `a: true
7171+ b: "2"
7272+ c: 3
7373+ `,
7474+ `a: true, b: "2", c: 3`,
7575+ }, {
7676+ "emitted refrencing non-emitted",
7777+ `a: 1
7878+ b: "2"
7979+ c: 3
8080+ { name: b, total: a + b }`,
8181+ `a: 1, b: "2", c: 3, {name: b, total: a+b}`,
8282+ }, {
8383+ "package file",
8484+ `package k8s
8585+ {}
8686+ `,
8787+ `package k8s, {}`,
8888+ }, {
8989+ "imports group",
9090+ `package k8s
9191+9292+ import (
9393+ a "foo"
9494+ "bar/baz"
9595+ . "model"
9696+ )
9797+ `,
9898+ `package k8s, import ( a "foo", "bar/baz", . "model" )`,
9999+ }, {
100100+ "imports single",
101101+ `package k8s
102102+103103+ import a "foo"
104104+ import "bar/baz"
105105+ import . "model"
106106+ `,
107107+ `package k8s, import a "foo", import "bar/baz", import . "model"`,
108108+ }, {
109109+ "collapsed fields",
110110+ `a b c: 1
111111+ // job foo { bar: 1 } // TODO error after foo
112112+ job "foo": { bar: 1 }
113113+ `,
114114+ `a: {b: {c: 1}}, job: {"foo": {bar: 1}}`,
115115+ }, {
116116+ "identifiers",
117117+ `// $_: 1,
118118+ a: {b: {c: d}}
119119+ c: a
120120+ d: a.b
121121+ // e: a."b" // TODO: is an error
122122+ e: a.b.c
123123+ "f": f,
124124+ <X>: X
125125+ `,
126126+ "a: {b: {c: d}}, c: a, d: a.b, e: a.b.c, \"f\": f, <X>: X",
127127+ }, {
128128+ "expressions",
129129+ ` a: (2 + 3) * 5
130130+ b: (2 + 3) + 4
131131+ c: 2 + 3 + 4
132132+ d: -1
133133+ e: !foo
134134+ f: _|_
135135+ `,
136136+ "a: (2+3)*5, b: (2+3)+4, c: 2+3+4, d: -1, e: !foo, f: _|_",
137137+ }, {
138138+ "pseudo keyword expressions",
139139+ ` a: (2 div 3) mod 5
140140+ b: (2 quo 3) rem 4
141141+ c: 2 div 3 div 4
142142+ `,
143143+ "a: (2 div 3) mod 5, b: (2 quo 3) rem 4, c: 2 div 3 div 4",
144144+ }, {
145145+ "ranges",
146146+ ` a: 1..2
147147+ b: 2.0 .. 40.0
148148+ c: "a".."b"
149149+ v: (1..2)..(5..10)
150150+ w: 1..2..3
151151+ d: 3T..5M
152152+ `,
153153+ "a: 1..2, b: 2.0..40.0, c: \"a\"..\"b\", v: (1..2)..(5..10), w: 1..2..3, d: 3T..5M",
154154+ }, {
155155+ "indices",
156156+ `{
157157+ a: b[2]
158158+ b: c[1:2]
159159+ c: "asdf"
160160+ d: c ["a"]
161161+ }`,
162162+ `{a: b[2], b: c[1:2], c: "asdf", d: c["a"]}`,
163163+ }, {
164164+ "lambdas",
165165+ `{
166166+ a(P, Q, r: R) -> { p: P, q: Q }
167167+ b: a(4002, "s")
168168+ }`,
169169+ `{a: (P: _,Q: _,r: R,) -> {p: P, q: Q}, b: a(4002, "s")}`, // c(C): {d(D): {}}}`,
170170+ }, {
171171+ "calls",
172172+ `{
173173+ a: b(a.b, c.d)
174174+ b: a.b(c)
175175+ }`,
176176+ `{a: b(a.b, c.d), b: a.b(c)}`,
177177+ }, {
178178+ "lists",
179179+ `{
180180+ a: [ 1, 2, 3, b..., c... ]
181181+ b: [ 1, 2, 3, ],
182182+ c: [ 1,
183183+ 2,
184184+ 3
185185+ ],
186186+ d: [ 1+2, 2, 4,]
187187+ }`,
188188+ `{a: [1, 2, 3, b..., c...], b: [1, 2, 3], c: [1, 2, 3], d: [1+2, 2, 4]}`,
189189+ }, {
190190+ "list types",
191191+ `{
192192+ a: 4*[int]
193193+ b: 0..5*[ {a: 5} ]
194194+ c1: [...int]
195195+ c2: [...]
196196+ c3: [1, 2, ...int,]
197197+ }`,
198198+ `{a: 4*[int], b: 0..5*[{a: 5}], c1: [...int], c2: [...], c3: [1, 2, ...int]}`,
199199+ }, {
200200+ "list comprehensions",
201201+ `{
202202+ y: [1,2,3]
203203+ b: [ x for x in y if x == 1 ],
204204+ }`,
205205+ `{y: [1, 2, 3], b: [x for x in y if x==1 ]}`,
206206+ }, {
207207+ "field comprehensions",
208208+ `{
209209+ y: { a: 1, b: 2}
210210+ a: { "\(k)": v for k, v in y if v > 2 }
211211+ }`,
212212+ `{y: {a: 1, b: 2}, a: {"\(k)": v for k: v in y if v>2 }}`,
213213+ }, {
214214+ "duplicates allowed",
215215+ `{
216216+ a b: 3
217217+ a: { b: 3 }
218218+ }`,
219219+ "{a: {b: 3}, a: {b: 3}}",
220220+ }, {
221221+ "templates",
222222+ `{
223223+ <foo>: { a: int }
224224+ a: { a: 1 }
225225+ }`,
226226+ "{<foo>: {a: int}, a: {a: 1}}",
227227+ }, {
228228+ "foo",
229229+ `[
230230+ [1],
231231+ [1, 2],
232232+ [1, 2, 3],
233233+ ]`,
234234+ "[[1], [1, 2], [1, 2, 3]]",
235235+ }, {
236236+ "interpolation",
237237+ `a: "foo \(ident)"
238238+ b: "bar \(bar) $$$ "
239239+ c: "nest \( { a: "\( nest ) "}.a ) \(5)"
240240+ m1: """
241241+ multi \(bar)
242242+ """
243243+ m2: '''
244244+ \(bar) multi
245245+ '''`,
246246+ `a: "foo \(ident)", b: "bar \(bar) $$$ ", c: "nest \({a: "\(nest) "}.a) \(5)", ` + "m1: \"\"\"\n\t\t\t multi \\(bar)\n\t\t\t \"\"\", m2: '''\n\t\t\t \\(bar) multi\n\t\t\t '''",
247247+ }, {
248248+ "file comments",
249249+ `// foo
250250+251251+ // uni
252252+ package foo // uniline
253253+254254+ // file.1
255255+ // file.2
256256+257257+ `,
258258+ "<[0// foo] [d0// uni] [l3// uniline] [3// file.1 // file.2] package foo, >",
259259+ }, {
260260+ "line comments",
261261+ `// doc
262262+ a: 5 // line
263263+ b: 6 // lineb
264264+ // next
265265+ `, // next is followed by EOF. Ensure it doesn't move to file.
266266+ "<[d0// doc] [l4// line] a: 5>, " +
267267+ "<[l4// lineb] [4// next] b: 6>",
268268+ }, {
269269+ "alt comments",
270270+ `// a ...
271271+ a: 5 // line a
272272+273273+ // about a
274274+275275+ // b ...
276276+ b: // lineb
277277+ 6
278278+279279+ // about b
280280+281281+ c: 7
282282+283283+ // about c
284284+285285+ `,
286286+ "<[d0// a ...] [l4// line a] [4// about a] a: 5>, " +
287287+ "<[d0// b ...] [l2// lineb] [4// about b] b: 6>, " +
288288+ "<[4// about c] c: 7>",
289289+ }, {
290290+ "expr comments",
291291+ `
292292+ a: 2 + // 2 +
293293+ 3 + // 3 +
294294+ 4 // 4
295295+ l1( // sig
296296+ ) -> // arrow
297297+ 4 // expr
298298+ l2(a // la
299299+ ) -> // arrow
300300+ a // l2
301301+ l3(
302302+ // param a
303303+ a : // la
304304+305305+ // int
306306+ int // lint
307307+ ) -> // larrow
308308+ a + 1
309309+ `,
310310+ "<[l4// 4] a: <[l2// 3 +] <[l2// 2 +] 2+3>+4>>, " +
311311+ "<[l4// expr] l1: <[l1// sig] [l4// arrow] () -> 4>>, " +
312312+ "<[l4// l2] l2: <[l4// arrow] (<[l1// la] a: _>,) -> a>>, " +
313313+ "l3: <[l4// larrow] (<[l1// la] [l3// lint] <[d0// param a] a>: <[d0// int] int>>,) -> a+1>",
314314+ }, {
315315+ "composit comments",
316316+ `a : {
317317+ a: 1, b: 2, c: 3, d: 4
318318+ // end
319319+ }
320320+ b: [
321321+ 1, 2, 3, 4, 5,
322322+ // end
323323+ ]
324324+ c: [ 1, 2, 3, 4, // here
325325+ 5, 6, 7, 8 // and here
326326+ ]
327327+ d: {
328328+ a: /* 8 */ 1 // Hello
329329+ // Doc
330330+ b: 2
331331+ }
332332+ e1: [
333333+ // comment in list body
334334+ ]
335335+ e2: {
336336+ // comment in struct body
337337+ }
338338+ `,
339339+ "a: <[d2// end] {a: 1, b: 2, c: 3, d: 4}>, " +
340340+ "b: <[d2// end] [1, 2, 3, 4, 5]>, " +
341341+ "c: [1, 2, 3, <[l1// here] 4>, 5, 6, 7, <[l1// and here] 8>], " +
342342+ "d: {<[2/* 8 */] [l4// Hello] a: 1>, <[d0// Doc] b: 2>}, " +
343343+ "e1: <[d2// comment in list body] []>, " +
344344+ "e2: <[d1// comment in struct body] {}>",
345345+ }, {
346346+ "emit comments",
347347+ `// a comment at the beginning of the file
348348+349349+ // a second comment
350350+351351+ // comment
352352+ a: 5
353353+354354+ {}
355355+356356+ // a comment at the end of the file
357357+ `,
358358+ "<[0// a comment at the beginning of the file] [0// a second comment] <[d0// comment] a: 5>, <[2// a comment at the end of the file] {}>>",
359359+ }}
360360+ for _, tc := range testCases {
361361+ t.Run(tc.desc, func(t *testing.T) {
362362+ fset := token.NewFileSet()
363363+ mode := []Option{AllErrors, ParseLambdas}
364364+ if strings.Contains(tc.desc, "comments") {
365365+ mode = append(mode, ParseComments)
366366+ }
367367+ f, err := ParseFile(fset, "input", tc.in, mode...)
368368+ if err != nil {
369369+ t.Errorf("unexpected error: %v", err)
370370+ }
371371+ if got := debugStr(f); got != tc.out {
372372+ t.Errorf("\ngot %q;\nwant %q", got, tc.out)
373373+ }
374374+ })
375375+ }
376376+}
377377+378378+func TestParseExpr(t *testing.T) {
379379+ // just kicking the tires:
380380+ // a valid arithmetic expression
381381+ src := "a + b"
382382+ x, err := parseExprString(src)
383383+ if err != nil {
384384+ t.Errorf("ParseExpr(%q): %v", src, err)
385385+ }
386386+ // sanity check
387387+ if _, ok := x.(*ast.BinaryExpr); !ok {
388388+ t.Errorf("ParseExpr(%q): got %T, want *BinaryExpr", src, x)
389389+ }
390390+391391+ // an invalid expression
392392+ src = "a + *"
393393+ if _, err := parseExprString(src); err == nil {
394394+ t.Errorf("ParseExpr(%q): got no error", src)
395395+ }
396396+397397+ // a comma is not permitted unless automatically inserted
398398+ src = "a + b\n"
399399+ if _, err := parseExprString(src); err != nil {
400400+ t.Errorf("ParseExpr(%q): got error %s", src, err)
401401+ }
402402+ src = "a + b;"
403403+ if _, err := parseExprString(src); err == nil {
404404+ t.Errorf("ParseExpr(%q): got no error", src)
405405+ }
406406+407407+ // various other stuff following a valid expression
408408+ const validExpr = "a + b"
409409+ const anything = "dh3*#D)#_"
410410+ for _, c := range "!)]};," {
411411+ src := validExpr + string(c) + anything
412412+ if _, err := parseExprString(src); err == nil {
413413+ t.Errorf("ParseExpr(%q): got no error", src)
414414+ }
415415+ }
416416+417417+ // ParseExpr must not crash
418418+ for _, src := range valids {
419419+ parseExprString(src)
420420+ }
421421+}
422422+423423+func TestImports(t *testing.T) {
424424+ var imports = map[string]bool{
425425+ `"a"`: true,
426426+ `"a/b"`: true,
427427+ `"a.b"`: true,
428428+ `"m\x61th"`: true,
429429+ `"greek/αβ"`: true,
430430+ `""`: false,
431431+432432+ // Each of these pairs tests both `` vs "" strings
433433+ // and also use of invalid characters spelled out as
434434+ // escape sequences and written directly.
435435+ // For example `"\x00"` tests import "\x00"
436436+ // while "`\x00`" tests import `<actual-NUL-byte>`.
437437+ "`a`": true,
438438+ `"\x00"`: false,
439439+ "`\x00`": false,
440440+ `"\x7f"`: false,
441441+ "`\x7f`": false,
442442+ `"a!"`: false,
443443+ "`a!`": false,
444444+ `"a b"`: false,
445445+ "`a b`": false,
446446+ `"a\\b"`: false,
447447+ "`a\\b`": false,
448448+ "\"`a`\"": false,
449449+ "`\"a\"`": false,
450450+ `"\x80\x80"`: false,
451451+ "`\x80\x80`": false,
452452+ `"\xFFFD"`: false,
453453+ "`\xFFFD`": false,
454454+ }
455455+ for path, isValid := range imports {
456456+ t.Run(path, func(t *testing.T) {
457457+ src := fmt.Sprintf("package p, import %s", path)
458458+ _, err := ParseFile(token.NewFileSet(), "", src)
459459+ switch {
460460+ case err != nil && isValid:
461461+ t.Errorf("ParseFile(%s): got %v; expected no error", src, err)
462462+ case err == nil && !isValid:
463463+ t.Errorf("ParseFile(%s): got no error; expected one", src)
464464+ }
465465+ })
466466+ }
467467+}
468468+469469+func labelName(l ast.Label) string {
470470+ name, _ := ast.LabelName(l)
471471+ return name
472472+}
473473+474474+func getField(file *ast.File, fieldname string) *ast.Field {
475475+ get := func(elts []ast.Decl, name string) *ast.Field {
476476+ for _, s := range elts {
477477+ if s, ok := s.(*ast.Field); ok && labelName(s.Label) == name {
478478+ return s
479479+ }
480480+ }
481481+ return nil
482482+ }
483483+ elts := file.Decls
484484+ var m *ast.Field
485485+ for _, p := range strings.Split(fieldname, ".") {
486486+ m = get(elts, p)
487487+ if v, ok := m.Value.(*ast.StructLit); ok {
488488+ elts = v.Elts
489489+ } else {
490490+ break
491491+ }
492492+ }
493493+ return m
494494+}
495495+496496+// Don't use CommentGroup.Text() - we want to see exact comment text.
497497+func commentText(c *ast.CommentGroup) string {
498498+ var buf bytes.Buffer
499499+ if c != nil {
500500+ for _, c := range c.List {
501501+ buf.WriteString(c.Text)
502502+ }
503503+ }
504504+ return buf.String()
505505+}
506506+507507+// TestIncompleteSelection ensures that an incomplete selector
508508+// expression is parsed as a (blank) *SelectorExpr, not a
509509+// *BadExpr.
510510+func TestIncompleteSelection(t *testing.T) {
511511+ for _, src := range []string{
512512+ "{ a: fmt. }", // at end of object
513513+ "{ a: fmt.\n\"a\": x }", // not at end of struct
514514+ } {
515515+ t.Run("", func(t *testing.T) {
516516+ fset := token.NewFileSet()
517517+ f, err := ParseFile(fset, "", src)
518518+ if err == nil {
519519+ t.Fatalf("ParseFile(%s) succeeded unexpectedly", src)
520520+ }
521521+522522+ const wantErr = "expected selector"
523523+ if !strings.Contains(err.Error(), wantErr) {
524524+ t.Errorf("ParseFile returned wrong error %q, want %q", err, wantErr)
525525+ }
526526+527527+ var sel *ast.SelectorExpr
528528+ ast.Walk(f, func(n ast.Node) bool {
529529+ if n, ok := n.(*ast.SelectorExpr); ok {
530530+ sel = n
531531+ }
532532+ return true
533533+ }, nil)
534534+ if sel == nil {
535535+ t.Fatalf("found no *SelectorExpr: %#v %s", f.Decls[0], debugStr(f))
536536+ }
537537+ const wantSel = "&{{<nil>} fmt _}"
538538+ if fmt.Sprint(sel) != wantSel {
539539+ t.Fatalf("found selector %v, want %s", sel, wantSel)
540540+ }
541541+ })
542542+ }
543543+}
+41
cue/parser/performance_test.go
···11+// Copyright 2018 The CUE Authors
22+//
33+// Licensed under the Apache License, Version 2.0 (the "License");
44+// you may not use this file except in compliance with the License.
55+// You may obtain a copy of the License at
66+//
77+// http://www.apache.org/licenses/LICENSE-2.0
88+//
99+// Unless required by applicable law or agreed to in writing, software
1010+// distributed under the License is distributed on an "AS IS" BASIS,
1111+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1212+// See the License for the specific language governing permissions and
1313+// limitations under the License.
1414+1515+package parser
1616+1717+import (
1818+ "io/ioutil"
1919+ "testing"
2020+2121+ "cuelang.org/go/cue/token"
2222+)
2323+2424+var src = readFile("testdata/commas.src")
2525+2626+func readFile(filename string) []byte {
2727+ data, err := ioutil.ReadFile(filename)
2828+ if err != nil {
2929+ panic(err)
3030+ }
3131+ return data
3232+}
3333+3434+func BenchmarkParse(b *testing.B) {
3535+ b.SetBytes(int64(len(src)))
3636+ for i := 0; i < b.N; i++ {
3737+ if _, err := ParseFile(token.NewFileSet(), "", src, ParseComments); err != nil {
3838+ b.Fatalf("benchmark failed due to parse error: %s", err)
3939+ }
4040+ }
4141+}
+302
cue/parser/print.go
···11+// Copyright 2018 The CUE Authors
22+//
33+// Licensed under the Apache License, Version 2.0 (the "License");
44+// you may not use this file except in compliance with the License.
55+// You may obtain a copy of the License at
66+//
77+// http://www.apache.org/licenses/LICENSE-2.0
88+//
99+// Unless required by applicable law or agreed to in writing, software
1010+// distributed under the License is distributed on an "AS IS" BASIS,
1111+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1212+// See the License for the specific language governing permissions and
1313+// limitations under the License.
1414+1515+package parser
1616+1717+import (
1818+ "fmt"
1919+ "strconv"
2020+ "strings"
2121+2222+ "cuelang.org/go/cue/ast"
2323+ "cuelang.org/go/cue/token"
2424+ "cuelang.org/go/internal"
2525+)
2626+2727+func init() {
2828+ internal.DebugStr = debugStr
2929+}
3030+3131+func debugStr(x interface{}) (out string) {
3232+ if n, ok := x.(ast.Node); ok {
3333+ comments := ""
3434+ for _, g := range n.Comments() {
3535+ comments += debugStr(g)
3636+ }
3737+ if comments != "" {
3838+ defer func() { out = "<" + comments + out + ">" }()
3939+ }
4040+ }
4141+ switch v := x.(type) {
4242+ case *ast.File:
4343+ out := ""
4444+ if v.Name != nil {
4545+ out += "package "
4646+ out += debugStr(v.Name)
4747+ out += ", "
4848+ }
4949+ out += debugStr(v.Decls)
5050+ return out
5151+5252+ case *ast.Alias:
5353+ out := debugStr(v.Ident)
5454+ out += " = "
5555+ out += debugStr(v.Expr)
5656+ return out
5757+5858+ case *ast.BottomLit:
5959+ return "_|_"
6060+6161+ case *ast.BasicLit:
6262+ return v.Value
6363+6464+ case *ast.Interpolation:
6565+ for _, e := range v.Elts {
6666+ out += debugStr(e)
6767+ }
6868+ return out
6969+7070+ case *ast.EmitDecl:
7171+ // out := "<"
7272+ out += debugStr(v.Expr)
7373+ // out += ">"
7474+ return out
7575+7676+ case *ast.ImportDecl:
7777+ out := "import "
7878+ if v.Lparen != token.NoPos {
7979+ out += "( "
8080+ out += debugStr(v.Specs)
8181+ out += " )"
8282+ } else {
8383+ out += debugStr(v.Specs)
8484+ }
8585+ return out
8686+8787+ case *ast.ComprehensionDecl:
8888+ out := debugStr(v.Field)
8989+ out += " "
9090+ out += debugStr(v.Clauses)
9191+ return out
9292+9393+ case *ast.StructLit:
9494+ out := "{"
9595+ out += debugStr(v.Elts)
9696+ out += "}"
9797+ return out
9898+9999+ case *ast.ListLit:
100100+ out := "["
101101+ out += debugStr(v.Elts)
102102+ if v.Ellipsis != token.NoPos || v.Type != nil {
103103+ if out != "[" {
104104+ out += ", "
105105+ }
106106+ out += "..."
107107+ if v.Type != nil {
108108+ out += debugStr(v.Type)
109109+ }
110110+ }
111111+ out += "]"
112112+ return out
113113+114114+ case *ast.ListComprehension:
115115+ out := "["
116116+ out += debugStr(v.Expr)
117117+ out += " "
118118+ out += debugStr(v.Clauses)
119119+ out += "]"
120120+ return out
121121+122122+ case *ast.ForClause:
123123+ out := "for "
124124+ if v.Key != nil {
125125+ out += debugStr(v.Key)
126126+ out += ": "
127127+ }
128128+ out += debugStr(v.Value)
129129+ out += " in "
130130+ out += debugStr(v.Source)
131131+ return out
132132+133133+ case *ast.IfClause:
134134+ out := "if "
135135+ out += debugStr(v.Condition)
136136+ return out
137137+138138+ case *ast.Field:
139139+ out := debugStr(v.Label)
140140+ if v.Value != nil {
141141+ out += ": "
142142+ out += debugStr(v.Value)
143143+ }
144144+ return out
145145+146146+ case *ast.LambdaExpr:
147147+ out := "("
148148+ for _, m := range v.Params {
149149+ out += debugStr(m)
150150+ out += ","
151151+ }
152152+ out += ") -> "
153153+ out += debugStr(v.Expr)
154154+ return out
155155+156156+ case *ast.Ident:
157157+ return v.Name
158158+159159+ case *ast.ExprLabel:
160160+ out := "["
161161+ out += debugStr(v.Label)
162162+ out += "]"
163163+ return out
164164+165165+ case *ast.TemplateLabel:
166166+ out := "<"
167167+ out += debugStr(v.Ident)
168168+ out += ">"
169169+ return out
170170+171171+ case *ast.SelectorExpr:
172172+ return debugStr(v.X) + "." + debugStr(v.Sel)
173173+174174+ case *ast.CallExpr:
175175+ out := debugStr(v.Fun)
176176+ out += "("
177177+ out += debugStr(v.Args)
178178+ out += ")"
179179+ return out
180180+181181+ case *ast.Ellipsis:
182182+ return debugStr(v.Elt) + "..."
183183+184184+ case *ast.ParenExpr:
185185+ out := "("
186186+ out += debugStr(v.X)
187187+ out += ")"
188188+ return out
189189+190190+ case *ast.UnaryExpr:
191191+ return v.Op.String() + debugStr(v.X)
192192+193193+ case *ast.BinaryExpr:
194194+ out := debugStr(v.X)
195195+ op := v.Op.String()
196196+ if 'a' <= op[0] && op[0] <= 'z' {
197197+ op = fmt.Sprintf(" %s ", op)
198198+ }
199199+ out += op
200200+ out += debugStr(v.Y)
201201+ return out
202202+203203+ case []*ast.CommentGroup:
204204+ var a []string
205205+ for _, c := range v {
206206+ a = append(a, debugStr(c))
207207+ }
208208+ return strings.Join(a, "\n")
209209+210210+ case *ast.CommentGroup:
211211+ str := "["
212212+ if v.Doc {
213213+ str += "d"
214214+ }
215215+ if v.Line {
216216+ str += "l"
217217+ }
218218+ str += strconv.Itoa(int(v.Position))
219219+ var a = []string{}
220220+ for _, c := range v.List {
221221+ a = append(a, c.Text)
222222+ }
223223+ return str + strings.Join(a, " ") + "] "
224224+225225+ case *ast.IndexExpr:
226226+ out := debugStr(v.X)
227227+ out += "["
228228+ out += debugStr(v.Index)
229229+ out += "]"
230230+ return out
231231+232232+ case *ast.SliceExpr:
233233+ out := debugStr(v.X)
234234+ out += "["
235235+ out += debugStr(v.Low)
236236+ out += ":"
237237+ out += debugStr(v.High)
238238+ out += "]"
239239+ return out
240240+241241+ case *ast.ImportSpec:
242242+ out := ""
243243+ if v.Name != nil {
244244+ out += debugStr(v.Name)
245245+ out += " "
246246+ }
247247+ out += debugStr(v.Path)
248248+ return out
249249+250250+ case []ast.Decl:
251251+ if len(v) == 0 {
252252+ return ""
253253+ }
254254+ out := ""
255255+ for _, d := range v {
256256+ out += debugStr(d)
257257+ out += sep
258258+ }
259259+ return out[:len(out)-len(sep)]
260260+261261+ case []ast.Clause:
262262+ if len(v) == 0 {
263263+ return ""
264264+ }
265265+ out := ""
266266+ for _, c := range v {
267267+ out += debugStr(c)
268268+ out += " "
269269+ }
270270+ return out
271271+272272+ case []ast.Expr:
273273+ if len(v) == 0 {
274274+ return ""
275275+ }
276276+ out := ""
277277+ for _, d := range v {
278278+ out += debugStr(d)
279279+ out += sep
280280+ }
281281+ return out[:len(out)-len(sep)]
282282+283283+ case []*ast.ImportSpec:
284284+ if len(v) == 0 {
285285+ return ""
286286+ }
287287+ out := ""
288288+ for _, d := range v {
289289+ out += debugStr(d)
290290+ out += sep
291291+ }
292292+ return out[:len(out)-len(sep)]
293293+294294+ default:
295295+ if v == nil {
296296+ return ""
297297+ }
298298+ return fmt.Sprintf("<%T>", x)
299299+ }
300300+}
301301+302302+const sep = ", "
+209
cue/parser/resolve.go
···11+// Copyright 2018 The CUE Authors
22+//
33+// Licensed under the Apache License, Version 2.0 (the "License");
44+// you may not use this file except in compliance with the License.
55+// You may obtain a copy of the License at
66+//
77+// http://www.apache.org/licenses/LICENSE-2.0
88+//
99+// Unless required by applicable law or agreed to in writing, software
1010+// distributed under the License is distributed on an "AS IS" BASIS,
1111+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1212+// See the License for the specific language governing permissions and
1313+// limitations under the License.
1414+1515+// This file implements scopes and the objects they contain.
1616+1717+package parser
1818+1919+import (
2020+ "bytes"
2121+ "fmt"
2222+2323+ "cuelang.org/go/cue/ast"
2424+ "cuelang.org/go/cue/token"
2525+)
2626+2727+// resolve resolves all identifiers in a file. Unresolved identifiers are
2828+// recorded in Unresolved.
2929+func resolve(f *ast.File, errFn func(pos token.Pos, msg string)) {
3030+ walk(&scope{errFn: errFn}, f)
3131+}
3232+3333+// A Scope maintains the set of named language entities declared
3434+// in the scope and a link to the immediately surrounding (outer)
3535+// scope.
3636+//
3737+type scope struct {
3838+ file *ast.File
3939+ outer *scope
4040+ node ast.Node
4141+ index map[string]ast.Node
4242+4343+ errFn func(p token.Pos, msg string)
4444+}
4545+4646+func newScope(f *ast.File, outer *scope, node ast.Node, decls []ast.Decl) *scope {
4747+ const n = 4 // initial scope capacity
4848+ s := &scope{
4949+ file: f,
5050+ outer: outer,
5151+ node: node,
5252+ index: make(map[string]ast.Node, n),
5353+ errFn: outer.errFn,
5454+ }
5555+ for _, d := range decls {
5656+ switch x := d.(type) {
5757+ case *ast.Field:
5858+ if name, ok := ast.LabelName(x.Label); ok {
5959+ s.insert(name, x.Value)
6060+ }
6161+ case *ast.Alias:
6262+ name := x.Ident.Name
6363+ s.insert(name, x)
6464+ // Handle imports
6565+ }
6666+ }
6767+ return s
6868+}
6969+7070+func (s *scope) insert(name string, n ast.Node) {
7171+ if _, existing := s.lookup(name); existing != nil {
7272+ _, isAlias1 := n.(*ast.Alias)
7373+ _, isAlias2 := existing.(*ast.Alias)
7474+ if isAlias1 != isAlias2 {
7575+ s.errFn(n.Pos(), "cannot have alias and non-alias with the same name")
7676+ return
7777+ } else if isAlias1 || isAlias2 {
7878+ s.errFn(n.Pos(), "cannot have two aliases with the same name in the same scope")
7979+ return
8080+ }
8181+ }
8282+ s.index[name] = n
8383+}
8484+8585+func (s *scope) lookup(name string) (obj, node ast.Node) {
8686+ last := s
8787+ for s != nil {
8888+ if n, ok := s.index[name]; ok {
8989+ if last.node == n {
9090+ return nil, n
9191+ }
9292+ return s.node, n
9393+ }
9494+ s, last = s.outer, s
9595+ }
9696+ return nil, nil
9797+}
9898+9999+func (s *scope) After(n ast.Node) {}
100100+func (s *scope) Before(n ast.Node) (w visitor) {
101101+ switch x := n.(type) {
102102+ case *ast.File:
103103+ s := newScope(x, s, x, x.Decls)
104104+ // Support imports.
105105+ for _, d := range x.Decls {
106106+ walk(s, d)
107107+ }
108108+ return nil
109109+110110+ case *ast.StructLit:
111111+ return newScope(s.file, s, x, x.Elts)
112112+113113+ case *ast.ComprehensionDecl:
114114+ s = scopeClauses(s, x.Clauses)
115115+116116+ case *ast.ListComprehension:
117117+ s = scopeClauses(s, x.Clauses)
118118+119119+ case *ast.Field:
120120+ switch label := x.Label.(type) {
121121+ case *ast.Interpolation:
122122+ walk(s, label)
123123+ case *ast.ExprLabel:
124124+ walk(s, x.Label)
125125+ case *ast.TemplateLabel:
126126+ s := newScope(s.file, s, x, nil)
127127+ name, _ := ast.LabelName(label)
128128+ s.insert(name, x.Label) // Field used for entire lambda.
129129+ walk(s, x.Value)
130130+ return nil
131131+ }
132132+ // Disallow referring to the current LHS name (this applies recursively)
133133+ if x.Value != nil {
134134+ walk(s, x.Value)
135135+ }
136136+ return nil
137137+138138+ case *ast.Alias:
139139+ // Disallow referring to the current LHS name.
140140+ name := x.Ident.Name
141141+ saved := s.index[name]
142142+ delete(s.index, name) // The same name may still appear in another scope
143143+144144+ if x.Expr != nil {
145145+ walk(s, x.Expr)
146146+ }
147147+ s.index[name] = saved
148148+ return nil
149149+150150+ case *ast.ImportSpec:
151151+ return nil
152152+153153+ case *ast.SelectorExpr:
154154+ walk(s, x.X)
155155+ return nil
156156+157157+ case *ast.LambdaExpr:
158158+ s = newScope(s.file, s, x, nil)
159159+ for _, p := range x.Params {
160160+ name, _ := ast.LabelName(p.Label)
161161+ s.insert(name, p)
162162+ if p.Value == nil {
163163+ // TODO: make this optional
164164+ p.Value = ast.NewIdent("_")
165165+ s.insert(name, p)
166166+ }
167167+ }
168168+169169+ case *ast.Ident:
170170+ if obj, node := s.lookup(x.Name); node != nil {
171171+ x.Node = node
172172+ x.Scope = obj
173173+ } else {
174174+ s.file.Unresolved = append(s.file.Unresolved, x)
175175+ }
176176+ return nil
177177+ }
178178+ return s
179179+}
180180+181181+func scopeClauses(s *scope, clauses []ast.Clause) *scope {
182182+ for _, c := range clauses {
183183+ if f, ok := c.(*ast.ForClause); ok { // TODO(let): support let clause
184184+ walk(s, f.Source)
185185+ s = newScope(s.file, s, f, nil)
186186+ if f.Key != nil {
187187+ s.insert(f.Key.Name, f.Key)
188188+ }
189189+ s.insert(f.Value.Name, f.Value)
190190+ } else {
191191+ walk(s, c)
192192+ }
193193+ }
194194+ return s
195195+}
196196+197197+// Debugging support
198198+func (s *scope) String() string {
199199+ var buf bytes.Buffer
200200+ fmt.Fprintf(&buf, "scope %p {", s)
201201+ if s != nil && len(s.index) > 0 {
202202+ fmt.Fprintln(&buf)
203203+ for name := range s.index {
204204+ fmt.Fprintf(&buf, "\t%v\n", name)
205205+ }
206206+ }
207207+ fmt.Fprintf(&buf, "}\n")
208208+ return buf.String()
209209+}
+50
cue/parser/short_test.go
···11+// Copyright 2018 The CUE Authors
22+//
33+// Licensed under the Apache License, Version 2.0 (the "License");
44+// you may not use this file except in compliance with the License.
55+// You may obtain a copy of the License at
66+//
77+// http://www.apache.org/licenses/LICENSE-2.0
88+//
99+// Unless required by applicable law or agreed to in writing, software
1010+// distributed under the License is distributed on an "AS IS" BASIS,
1111+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1212+// See the License for the specific language governing permissions and
1313+// limitations under the License.
1414+1515+// This file contains test cases for short valid and invalid programs.
1616+1717+package parser
1818+1919+import "testing"
2020+2121+var valids = []string{
2222+ "\n",
2323+ `{}`,
2424+ `{ foo: "fmt", bar: () -> { baz: fmt.Service("Hello, World!") }, }`,
2525+ `{ <Name>: foo }`,
2626+ `{ a: 3 }`,
2727+}
2828+2929+func TestValid(t *testing.T) {
3030+ for _, src := range valids {
3131+ t.Run(src, func(t *testing.T) {
3232+ checkErrors(t, src, src)
3333+ })
3434+ }
3535+}
3636+3737+func TestInvalid(t *testing.T) {
3838+ invalids := []string{
3939+ `foo !/* ERROR "expected label or ':', found '!'" */`,
4040+ // `foo: /* ERROR "expected operand, found '}'" */}`, // TODO: wrong position
4141+ `{ <Name
4242+ /* ERROR "expected '>', found newline" */ >: foo }`,
4343+ // TODO:
4444+ // `{ </* ERROR "expected identifier, found newline" */
4545+ // Name>: foo }`,
4646+ }
4747+ for _, src := range invalids {
4848+ checkErrors(t, src, src)
4949+ }
5050+}
+35
cue/parser/testdata/commas.src
···11+// Copyright 2018 The CUE Authors
22+//
33+// Licensed under the Apache License, Version 2.0 (the "License");
44+// you may not use this file except in compliance with the License.
55+// You may obtain a copy of the License at
66+//
77+// http://www.apache.org/licenses/LICENSE-2.0
88+//
99+// Unless required by applicable law or agreed to in writing, software
1010+// distributed under the License is distributed on an "AS IS" BASIS,
1111+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1212+// See the License for the specific language governing permissions and
1313+// limitations under the License.
1414+1515+// Test case for error messages/parser synchronization
1616+// after missing commas.
1717+package foo
1818+1919+import "path/to/pkg"
2020+import name "path/to/pkg"
2121+import . "path/to/pkg"
2222+import /* ERROR "expected 'STRING', found newline" */
2323+import err /* ERROR "expected 'STRING', found newline" */
2424+2525+foo: [
2626+ 0 // legal JSON
2727+]
2828+2929+bar: [
3030+ 0,
3131+ 1,
3232+ 2,
3333+ 3
3434+]
3535+
···11+// Copyright 2018 The CUE Authors
22+//
33+// Licensed under the Apache License, Version 2.0 (the "License");
44+// you may not use this file except in compliance with the License.
55+// You may obtain a copy of the License at
66+//
77+// http://www.apache.org/licenses/LICENSE-2.0
88+//
99+// Unless required by applicable law or agreed to in writing, software
1010+// distributed under the License is distributed on an "AS IS" BASIS,
1111+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1212+// See the License for the specific language governing permissions and
1313+// limitations under the License.
1414+1515+package parser
1616+1717+import (
1818+ "fmt"
1919+2020+ "cuelang.org/go/cue/ast"
2121+ "cuelang.org/go/cue/token"
2222+)
2323+2424+// TODO: use ast.Walk or adopt that version to allow visitors.
2525+2626+// A visitor's before method is invoked for each node encountered by Walk.
2727+// If the result visitor w is not nil, Walk visits each of the children
2828+// of node with the visitor w, followed by a call of w.After.
2929+type visitor interface {
3030+ Before(node ast.Node) (w visitor)
3131+ After(node ast.Node)
3232+}
3333+3434+// Helper functions for common node lists. They may be empty.
3535+3636+func walkIdentList(v visitor, list []*ast.Ident) {
3737+ for _, x := range list {
3838+ walk(v, x)
3939+ }
4040+}
4141+4242+func walkExprList(v visitor, list []ast.Expr) {
4343+ for _, x := range list {
4444+ walk(v, x)
4545+ }
4646+}
4747+4848+func walkDeclList(v visitor, list []ast.Decl) {
4949+ for _, x := range list {
5050+ walk(v, x)
5151+ }
5252+}
5353+5454+// walk traverses an AST in depth-first order: It starts by calling
5555+// v.Visit(node); node must not be nil. If the visitor w returned by
5656+// v.Visit(node) is not nil, walk is invoked recursively with visitor
5757+// w for each of the non-nil children of node, followed by a call of
5858+// w.Visit(nil).
5959+//
6060+func walk(v visitor, node ast.Node) {
6161+ if v = v.Before(node); v == nil {
6262+ return
6363+ }
6464+6565+ // TODO: record the comment groups and interleave with the values like for
6666+ // parsing and printing?
6767+ for _, c := range node.Comments() {
6868+ walk(v, c)
6969+ }
7070+7171+ // walk children
7272+ // (the order of the cases matches the order
7373+ // of the corresponding node types in go)
7474+ switch n := node.(type) {
7575+ // Comments and fields
7676+ case *ast.Comment:
7777+ // nothing to do
7878+7979+ case *ast.CommentGroup:
8080+ for _, c := range n.List {
8181+ walk(v, c)
8282+ }
8383+8484+ case *ast.Field:
8585+ walk(v, n.Label)
8686+ if n.Value != nil {
8787+ walk(v, n.Value)
8888+ }
8989+9090+ case *ast.LambdaExpr:
9191+ for _, p := range n.Params {
9292+ walk(v, p)
9393+ }
9494+ walk(v, n.Expr)
9595+9696+ case *ast.StructLit:
9797+ for _, f := range n.Elts {
9898+ walk(v, f)
9999+ }
100100+101101+ // Expressions
102102+ case *ast.BottomLit, *ast.BadExpr, *ast.Ident, *ast.BasicLit:
103103+ // nothing to do
104104+105105+ case *ast.ExprLabel:
106106+ walk(v, n.Label)
107107+108108+ case *ast.TemplateLabel:
109109+ walk(v, n.Ident)
110110+111111+ case *ast.Interpolation:
112112+ for _, e := range n.Elts {
113113+ walk(v, e)
114114+ }
115115+116116+ case *ast.Ellipsis:
117117+ if n.Elt != nil {
118118+ walk(v, n.Elt)
119119+ }
120120+121121+ case *ast.ListLit:
122122+ walkExprList(v, n.Elts)
123123+ if n.Type != nil {
124124+ walk(v, n.Type)
125125+ }
126126+127127+ case *ast.ParenExpr:
128128+ walk(v, n.X)
129129+130130+ case *ast.SelectorExpr:
131131+ walk(v, n.X)
132132+ walk(v, n.Sel)
133133+134134+ case *ast.IndexExpr:
135135+ walk(v, n.X)
136136+ walk(v, n.Index)
137137+138138+ case *ast.SliceExpr:
139139+ walk(v, n.X)
140140+ if n.Low != nil {
141141+ walk(v, n.Low)
142142+ }
143143+ if n.High != nil {
144144+ walk(v, n.High)
145145+ }
146146+147147+ case *ast.CallExpr:
148148+ walk(v, n.Fun)
149149+ walkExprList(v, n.Args)
150150+151151+ case *ast.UnaryExpr:
152152+ walk(v, n.X)
153153+154154+ case *ast.BinaryExpr:
155155+ walk(v, n.X)
156156+ walk(v, n.Y)
157157+158158+ // Declarations
159159+ case *ast.ImportSpec:
160160+ if n.Name != nil {
161161+ walk(v, n.Name)
162162+ }
163163+ walk(v, n.Path)
164164+165165+ case *ast.BadDecl:
166166+ // nothing to do
167167+168168+ case *ast.ImportDecl:
169169+ for _, s := range n.Specs {
170170+ walk(v, s)
171171+ }
172172+173173+ case *ast.EmitDecl:
174174+ walk(v, n.Expr)
175175+176176+ case *ast.Alias:
177177+ walk(v, n.Ident)
178178+ walk(v, n.Expr)
179179+180180+ case *ast.ComprehensionDecl:
181181+ walk(v, n.Field)
182182+ for _, c := range n.Clauses {
183183+ walk(v, c)
184184+ }
185185+186186+ // Files and packages
187187+ case *ast.File:
188188+ if n.Name != nil {
189189+ walk(v, n.Name)
190190+ }
191191+ walkDeclList(v, n.Decls)
192192+ // don't walk n.Comments - they have been
193193+ // visited already through the individual
194194+ // nodes
195195+196196+ case *ast.ListComprehension:
197197+ walk(v, n.Expr)
198198+ for _, c := range n.Clauses {
199199+ walk(v, c)
200200+ }
201201+202202+ case *ast.ForClause:
203203+ if n.Key != nil {
204204+ walk(v, n.Key)
205205+ }
206206+ walk(v, n.Value)
207207+ walk(v, n.Source)
208208+209209+ case *ast.IfClause:
210210+ walk(v, n.Condition)
211211+212212+ default:
213213+ panic(fmt.Sprintf("Walk: unexpected node type %T", n))
214214+ }
215215+216216+ v.After(node)
217217+}
218218+219219+type inspector struct {
220220+ before func(ast.Node) bool
221221+ after func(ast.Node)
222222+223223+ commentStack []commentFrame
224224+ current commentFrame
225225+}
226226+227227+type commentFrame struct {
228228+ cg []*ast.CommentGroup
229229+ pos int8
230230+}
231231+232232+func (f *inspector) Before(node ast.Node) visitor {
233233+ if f.before == nil || f.before(node) {
234234+ f.commentStack = append(f.commentStack, f.current)
235235+ f.current = commentFrame{cg: node.Comments()}
236236+ f.visitComments(f.current.pos)
237237+ return f
238238+ }
239239+ return nil
240240+}
241241+242242+func (f *inspector) After(node ast.Node) {
243243+ f.visitComments(127)
244244+ p := len(f.commentStack) - 1
245245+ f.current = f.commentStack[p]
246246+ f.commentStack = f.commentStack[:p]
247247+ f.current.pos++
248248+ if f.after != nil {
249249+ f.after(node)
250250+ }
251251+}
252252+253253+func (f *inspector) Token(t token.Token) {
254254+ f.current.pos++
255255+}
256256+257257+func (f *inspector) setPos(i int8) {
258258+ f.current.pos = i
259259+}
260260+261261+func (f *inspector) visitComments(pos int8) {
262262+ c := &f.current
263263+ for ; len(c.cg) > 0; c.cg = c.cg[1:] {
264264+ cg := c.cg[0]
265265+ if cg.Position == pos {
266266+ continue
267267+ }
268268+ if f.before == nil || f.before(cg) {
269269+ for _, c := range cg.List {
270270+ if f.before == nil || f.before(c) {
271271+ if f.after != nil {
272272+ f.after(c)
273273+ }
274274+ }
275275+ }
276276+ if f.after != nil {
277277+ f.after(cg)
278278+ }
279279+ }
280280+ }
281281+}
+20
internal/internal.go
···11+// Copyright 2018 The CUE Authors
22+//
33+// Licensed under the Apache License, Version 2.0 (the "License");
44+// you may not use this file except in compliance with the License.
55+// You may obtain a copy of the License at
66+//
77+// http://www.apache.org/licenses/LICENSE-2.0
88+//
99+// Unless required by applicable law or agreed to in writing, software
1010+// distributed under the License is distributed on an "AS IS" BASIS,
1111+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1212+// See the License for the specific language governing permissions and
1313+// limitations under the License.
1414+1515+package internal // import "cuelang.org/go/internal"
1616+1717+// TODO: refactor packages as to make this package unnecessary.
1818+1919+// DebugStr prints a syntax node.
2020+var DebugStr func(x interface{}) string