1// Copyright 2018 The CUE Authors 2// 3// Licensed under the Apache License, Version 2.0 (the "License"); 4// you may not use this file except in compliance with the License. 5// You may obtain a copy of the License at 6// 7// http://www.apache.org/licenses/LICENSE-2.0 8// 9// Unless required by applicable law or agreed to in writing, software 10// distributed under the License is distributed on an "AS IS" BASIS, 11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12// See the License for the specific language governing permissions and 13// limitations under the License. 14 15// This file contains the exported entry points for invoking the 16 17package parser 18 19import ( 20 "cuelang.org/go/cue/ast" 21 "cuelang.org/go/cue/ast/astutil" 22 "cuelang.org/go/cue/errors" 23 "cuelang.org/go/cue/token" 24 "cuelang.org/go/internal/source" 25) 26 27// Option specifies a parse option. 28type Option func(p *parser) 29 30var ( 31 // PackageClauseOnly causes parsing to stop after the package clause. 32 PackageClauseOnly Option = packageClauseOnly 33 packageClauseOnly = func(p *parser) { 34 p.mode |= packageClauseOnlyMode 35 } 36 37 // ImportsOnly causes parsing to stop parsing after the import declarations. 38 ImportsOnly Option = importsOnly 39 importsOnly = func(p *parser) { 40 p.mode |= importsOnlyMode 41 } 42 43 // ParseComments causes comments to be parsed. 44 ParseComments Option = parseComments 45 parseComments = func(p *parser) { 46 p.mode |= parseCommentsMode 47 } 48 49 // Trace causes parsing to print a trace of parsed productions. 50 Trace Option = traceOpt 51 traceOpt = func(p *parser) { 52 p.mode |= traceMode 53 } 54 55 // DeclarationErrors causes parsing to report declaration errors. 56 DeclarationErrors Option = declarationErrors 57 declarationErrors = func(p *parser) { 58 p.mode |= declarationErrorsMode 59 } 60 61 // AllErrors causes all errors to be reported (not just the first 10 on different lines). 62 AllErrors Option = allErrors 63 allErrors = func(p *parser) { 64 p.mode |= allErrorsMode 65 } 66 67 // AllowPartial allows the parser to be used on a prefix buffer. 68 AllowPartial Option = allowPartial 69 allowPartial = func(p *parser) { 70 p.mode |= partialMode 71 } 72) 73 74// FromVersion specifies until which legacy version the parser should provide 75// backwards compatibility. 76func FromVersion(version int) Option { 77 if version >= 0 { 78 version++ 79 } 80 // Versions: 81 // <0: major version 0 (counting -1000 + x, where x = 100*m+p in 0.m.p 82 // >=0: x+1 in 1.x.y 83 return func(p *parser) { p.version = version } 84} 85 86func version0(minor, patch int) int { 87 return -1000 + 100*minor + patch 88} 89 90// DeprecationError is a sentinel error to indicate that an error is 91// related to an unsupported old CUE syntax. 92type DeprecationError struct { 93 Version int 94} 95 96func (e *DeprecationError) Error() string { 97 return "try running `cue fix` (possibly with an earlier version, like v0.2.2) to upgrade" 98} 99 100// Latest specifies the latest version of the parser, effectively setting 101// the strictest implementation. 102const Latest = latest 103 104const latest = -600 105 106// FileOffset specifies the File position info to use. 107func FileOffset(pos int) Option { 108 return func(p *parser) { p.offset = pos } 109} 110 111// A mode value is a set of flags (or 0). 112// They control the amount of source code parsed and other optional 113// parser functionality. 114type mode uint 115 116const ( 117 packageClauseOnlyMode mode = 1 << iota // stop parsing after package clause 118 importsOnlyMode // stop parsing after import declarations 119 parseCommentsMode // parse comments and add them to AST 120 partialMode 121 traceMode // print a trace of parsed productions 122 declarationErrorsMode // report declaration errors 123 allErrorsMode // report all errors (not just the first 10 on different lines) 124) 125 126// ParseFile parses the source code of a single CUE source file and returns 127// the corresponding File node. The source code may be provided via 128// the filename of the source file, or via the src parameter. 129// 130// If src != nil, ParseFile parses the source from src and the filename is 131// only used when recording position information. The type of the argument 132// for the src parameter must be string, []byte, or io.Reader. 133// If src == nil, ParseFile parses the file specified by filename. 134// 135// The mode parameter controls the amount of source text parsed and other 136// optional parser functionality. Position information is recorded in the 137// file set fset, which must not be nil. 138// 139// If the source couldn't be read, the returned AST is nil and the error 140// indicates the specific failure. If the source was read but syntax 141// errors were found, the result is a partial AST (with Bad* nodes 142// representing the fragments of erroneous source code). Multiple errors 143// are returned via a ErrorList which is sorted by file position. 144func ParseFile(filename string, src interface{}, mode ...Option) (f *ast.File, err error) { 145 146 // get source 147 text, err := source.Read(filename, src) 148 if err != nil { 149 return nil, err 150 } 151 152 var pp parser 153 defer func() { 154 if pp.panicking { 155 _ = recover() 156 } 157 158 // set result values 159 if f == nil { 160 // source is not a valid Go source file - satisfy 161 // ParseFile API and return a valid (but) empty 162 // *File 163 f = &ast.File{ 164 // Scope: NewScope(nil), 165 } 166 } 167 168 err = errors.Sanitize(pp.errors) 169 }() 170 171 // parse source 172 pp.init(filename, text, mode) 173 f = pp.parseFile() 174 if f == nil { 175 return nil, pp.errors 176 } 177 f.Filename = filename 178 astutil.Resolve(f, pp.errf) 179 180 return f, pp.errors 181} 182 183// ParseExpr is a convenience function for parsing an expression. 184// The arguments have the same meaning as for Parse, but the source must 185// be a valid CUE (type or value) expression. Specifically, fset must not 186// be nil. 187func ParseExpr(filename string, src interface{}, mode ...Option) (ast.Expr, error) { 188 // get source 189 text, err := source.Read(filename, src) 190 if err != nil { 191 return nil, err 192 } 193 194 var p parser 195 defer func() { 196 if p.panicking { 197 _ = recover() 198 } 199 err = errors.Sanitize(p.errors) 200 }() 201 202 // parse expr 203 p.init(filename, text, mode) 204 // Set up pkg-level scopes to avoid nil-pointer errors. 205 // This is not needed for a correct expression x as the 206 // parser will be ok with a nil topScope, but be cautious 207 // in case of an erroneous x. 208 e := p.parseRHS() 209 210 // If a comma was inserted, consume it; 211 // report an error if there's more tokens. 212 if p.tok == token.COMMA && p.lit == "\n" { 213 p.next() 214 } 215 if p.mode&partialMode == 0 { 216 p.expect(token.EOF) 217 } 218 219 if p.errors != nil { 220 return nil, p.errors 221 } 222 astutil.ResolveExpr(e, p.errf) 223 224 return e, p.errors 225} 226 227// parseExprString is a convenience function for obtaining the AST of an 228// expression x. The position information recorded in the AST is undefined. The 229// filename used in error messages is the empty string. 230func parseExprString(x string) (ast.Expr, error) { 231 return ParseExpr("", []byte(x)) 232} 233