Skip to content

Commit 47d6dce

Browse files
committed
internal/cueimports: export API and fix for CUE syntax
The original code still needed some adapting from the original Go version. Specifically: - it looked for semicolons not commas - it recognized the Go backquoted string syntax - it recognized the Go /*...*/ comment syntax Also, make it a little less general, as currently we don't seem to need the import-saving functionality (we can use `parser.ParseFile` with `CommentsOnly` which will keep us honest, even at a little bit of runtime cost), and because of that, we don't need to try to detect syntax errors. We can optimize at a later date if need be. Signed-off-by: Roger Peppe <[email protected]> Change-Id: I276087961c73d04222a115b462e01e130b4cd4e3 Reviewed-on: https://review.gerrithub.io/c/cue-lang/cue/+/1172690 TryBot-Result: CUEcueckoo <[email protected]> Unity-Result: CUE porcuepine <[email protected]> Reviewed-by: Daniel Martí <[email protected]>
1 parent 9e55783 commit 47d6dce

File tree

2 files changed

+56
-104
lines changed

2 files changed

+56
-104
lines changed

internal/cueimports/read.go

+20-44
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,4 @@
1-
//go:build ignore
2-
3-
// Copyright 2018 The CUE Authors
1+
// Copyright 2023 The CUE Authors
42
//
53
// Licensed under the Apache License, Version 2.0 (the "License");
64
// you may not use this file except in compliance with the License.
@@ -14,7 +12,9 @@
1412
// See the License for the specific language governing permissions and
1513
// limitations under the License.
1614

17-
package load
15+
// Package cueimports provides support for reading the import
16+
// section of a CUE file without needing to read the rest of it.
17+
package cueimports
1818

1919
import (
2020
"bufio"
@@ -76,7 +76,7 @@ func (r *importReader) readByte() byte {
7676
func (r *importReader) peekByte(skipSpace bool) byte {
7777
if r.err != nil {
7878
if r.nerr++; r.nerr > 10000 {
79-
panic("go/build: import reader looping")
79+
panic("import reader looping")
8080
}
8181
return 0
8282
}
@@ -90,10 +90,10 @@ func (r *importReader) peekByte(skipSpace bool) byte {
9090
}
9191
for r.err == nil && !r.eof {
9292
if skipSpace {
93-
// For the purposes of this reader, semicolons are never necessary to
93+
// For the purposes of this reader, commas are never necessary to
9494
// understand the input and are treated as spaces.
9595
switch c {
96-
case ' ', '\f', '\t', '\r', '\n', ';':
96+
case ' ', '\f', '\t', '\r', '\n', ',':
9797
c = r.readByte()
9898
continue
9999

@@ -103,14 +103,6 @@ func (r *importReader) peekByte(skipSpace bool) byte {
103103
for c != '\n' && r.err == nil && !r.eof {
104104
c = r.readByte()
105105
}
106-
} else if c == '*' {
107-
var c1 byte
108-
for (c != '*' || c1 != '/') && r.err == nil {
109-
if r.eof {
110-
r.syntaxError()
111-
}
112-
c, c1 = c1, r.readByte()
113-
}
114106
} else {
115107
r.syntaxError()
116108
}
@@ -161,29 +153,15 @@ func (r *importReader) readIdent() {
161153

162154
// readString reads a quoted string literal from the input.
163155
// If an identifier is not present, readString records a syntax error.
164-
func (r *importReader) readString(save *[]string) {
156+
func (r *importReader) readString() {
165157
switch r.nextByte(true) {
166-
case '`':
167-
start := len(r.buf) - 1
168-
for r.err == nil {
169-
if r.nextByte(false) == '`' {
170-
if save != nil {
171-
*save = append(*save, string(r.buf[start:]))
172-
}
173-
break
174-
}
175-
if r.eof {
176-
r.syntaxError()
177-
}
178-
}
179158
case '"':
180-
start := len(r.buf) - 1
159+
// Note: although the syntax in the specification only allows
160+
// a simple string literal here, the cue/parser package also
161+
// allows #"..."# and """ literals, so there's some impedance-mismatch here.
181162
for r.err == nil {
182163
c := r.nextByte(false)
183164
if c == '"' {
184-
if save != nil {
185-
*save = append(*save, string(r.buf[start:]))
186-
}
187165
break
188166
}
189167
if r.eof || c == '\n' {
@@ -200,19 +178,17 @@ func (r *importReader) readString(save *[]string) {
200178

201179
// readImport reads an import clause - optional identifier followed by quoted string -
202180
// from the input.
203-
func (r *importReader) readImport(imports *[]string) {
181+
func (r *importReader) readImport() {
204182
c := r.peekByte(true)
205-
if c == '.' {
206-
r.peek = 0
207-
} else if isIdent(c) {
183+
if isIdent(c) {
208184
r.readIdent()
209185
}
210-
r.readString(imports)
186+
r.readString()
211187
}
212188

213-
// readImports is like io.ReadAll, except that it expects a CUE file as
189+
// Read is like io.ReadAll, except that it expects a CUE file as
214190
// input and stops reading the input once the imports have completed.
215-
func readImports(f io.Reader, reportSyntaxError bool, imports *[]string) ([]byte, errors.Error) {
191+
func Read(f io.Reader) ([]byte, errors.Error) {
216192
r := &importReader{b: bufio.NewReader(f)}
217193

218194
r.readKeyword("package")
@@ -222,11 +198,11 @@ func readImports(f io.Reader, reportSyntaxError bool, imports *[]string) ([]byte
222198
if r.peekByte(true) == '(' {
223199
r.nextByte(false)
224200
for r.peekByte(true) != ')' && r.err == nil {
225-
r.readImport(imports)
201+
r.readImport()
226202
}
227203
r.nextByte(false)
228204
} else {
229-
r.readImport(imports)
205+
r.readImport()
230206
}
231207
}
232208

@@ -237,8 +213,8 @@ func readImports(f io.Reader, reportSyntaxError bool, imports *[]string) ([]byte
237213
}
238214

239215
// If we stopped for a syntax error, consume the whole file so that
240-
// we are sure we don't change the errors that go/parser returns.
241-
if r.err == errSyntax && !reportSyntaxError {
216+
// we are sure we don't change the errors that the cue/parser returns.
217+
if r.err == errSyntax {
242218
r.err = nil
243219
for r.err == nil && !r.eof {
244220
r.readByte()

internal/cueimports/read_test.go

+36-60
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
//go:build ignore
2-
31
// Copyright 2018 The CUE Authors
42
//
53
// Licensed under the Apache License, Version 2.0 (the "License");
@@ -14,7 +12,7 @@
1412
// See the License for the specific language governing permissions and
1513
// limitations under the License.
1614

17-
package load
15+
package cueimports
1816

1917
import (
2018
"io"
@@ -24,8 +22,6 @@ import (
2422
"cuelang.org/go/cue/errors"
2523
)
2624

27-
const quote = "`"
28-
2925
type readTest struct {
3026
// Test input contains ℙ where readImports should stop.
3127
in string
@@ -38,15 +34,15 @@ var readImportsTests = []readTest{
3834
"",
3935
},
4036
{
41-
`package p; import "x"`,
37+
`package p, import "x"`,
4238
"",
4339
},
4440
{
45-
`package p; import . "x"`,
41+
`package p, import . "x"`,
4642
"",
4743
},
4844
{
49-
`package p; import "x";ℙvar x = 1`,
45+
`package p, import "x",ℙvar x = 1`,
5046
"",
5147
},
5248
{
@@ -62,15 +58,12 @@ var readImportsTests = []readTest{
6258
"x"
6359
_ "x"
6460
a "x" // comment
65-
` + quote + `x` + quote + `
66-
_ ` + quote + `x` + quote + `
67-
a ` + quote + `x` + quote + `
6861
)
6962
import (
7063
)
7164
import ()
7265
import()import()import()
73-
import();import();import()
66+
import(),import(),import()
7467
7568
ℙvar x = 1
7669
`,
@@ -116,97 +109,80 @@ func testRead(t *testing.T, tests []readTest, read func(io.Reader) ([]byte, erro
116109

117110
func TestReadImports(t *testing.T) {
118111
testRead(t, readImportsTests, func(r io.Reader) ([]byte, errors.Error) {
119-
return readImports(r, true, nil)
112+
return Read(r)
120113
})
121114
}
122115

123116
var readFailuresTests = []readTest{
124117
{
125118
`package`,
126-
"syntax error",
119+
"",
127120
},
128121
{
129122
"package p\n\x00\nimport `math`\n",
130123
"unexpected NUL in input",
131124
},
132125
{
133-
`package p; import`,
134-
"syntax error",
126+
`package p, import`,
127+
"",
135128
},
136129
{
137-
`package p; import "`,
138-
"syntax error",
130+
`package p, import "`,
131+
"",
139132
},
140133
{
141-
"package p; import ` \n\n",
142-
"syntax error",
134+
"package p, import ` \n\n",
135+
"",
143136
},
144137
{
145-
`package p; import "x`,
146-
"syntax error",
138+
`package p, import "x`,
139+
"",
147140
},
148141
{
149-
`package p; import _`,
150-
"syntax error",
142+
`package p, import _`,
143+
"",
151144
},
152145
{
153-
`package p; import _ "`,
154-
"syntax error",
146+
`package p, import _ "`,
147+
"",
155148
},
156149
{
157-
`package p; import _ "x`,
158-
"syntax error",
150+
`package p, import _ "x`,
151+
"",
159152
},
160153
{
161-
`package p; import .`,
162-
"syntax error",
154+
`package p, import .`,
155+
"",
163156
},
164157
{
165-
`package p; import . "`,
166-
"syntax error",
158+
`package p, import . "`,
159+
"",
167160
},
168161
{
169-
`package p; import . "x`,
170-
"syntax error",
162+
`package p, import . "x`,
163+
"",
171164
},
172165
{
173-
`package p; import (`,
174-
"syntax error",
166+
`package p, import (`,
167+
"",
175168
},
176169
{
177-
`package p; import ("`,
178-
"syntax error",
170+
`package p, import ("`,
171+
"",
179172
},
180173
{
181-
`package p; import ("x`,
182-
"syntax error",
174+
`package p, import ("x`,
175+
"",
183176
},
184177
{
185-
`package p; import ("x"`,
186-
"syntax error",
178+
`package p, import ("x"`,
179+
"",
187180
},
188181
}
189182

190183
func TestReadFailures(t *testing.T) {
191184
// Errors should be reported (true arg to readImports).
192185
testRead(t, readFailuresTests, func(r io.Reader) ([]byte, errors.Error) {
193-
return readImports(r, true, nil)
194-
})
195-
}
196-
197-
func TestReadFailuresIgnored(t *testing.T) {
198-
// Syntax errors should not be reported (false arg to readImports).
199-
// Instead, entire file should be the output and no error.
200-
// Convert tests not to return syntax errors.
201-
tests := make([]readTest, len(readFailuresTests))
202-
copy(tests, readFailuresTests)
203-
for i := range tests {
204-
tt := &tests[i]
205-
if !strings.Contains(tt.err, "NUL") {
206-
tt.err = ""
207-
}
208-
}
209-
testRead(t, tests, func(r io.Reader) ([]byte, errors.Error) {
210-
return readImports(r, false, nil)
186+
return Read(r)
211187
})
212188
}

0 commit comments

Comments
 (0)