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