this repo has no description
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

cue/parser: add Config type

This makes the parser configuration public, which
makes it possible for `ParseFile` middleware such as
`cue/load.Config.ParseFile` to see what options have
been passed and modify them as desired.

Since `Config` implements `Option`, it will be straightforward
to call `parser.ParseFile` with a `Config` value.

This API change should be fully backwardly compatible.

Signed-off-by: Roger Peppe <rogpeppe@gmail.com>
Change-Id: I50fe0de41f9d2f1ff1107ea3bbae510860aae9f8
Reviewed-on: https://review.gerrithub.io/c/cue-lang/cue/+/1217123
TryBot-Result: CUEcueckoo <cueckoo@cuelang.org>
Reviewed-by: Daniel Martí <mvdan@mvdan.cc>
Reviewed-by: Marcel van Lohuizen <mpvl@gmail.com>
Unity-Result: CUE porcuepine <cue.porcuepine@gmail.com>

+119 -83
+108 -70
cue/parser/interface.go
··· 17 17 package parser 18 18 19 19 import ( 20 + "fmt" 21 + 20 22 "cuelang.org/go/cue/ast" 21 23 "cuelang.org/go/cue/ast/astutil" 22 24 "cuelang.org/go/cue/errors" 23 25 "cuelang.org/go/cue/token" 24 - "cuelang.org/go/internal" 26 + "cuelang.org/go/internal/cueversion" 27 + "cuelang.org/go/internal/mod/semver" 25 28 "cuelang.org/go/internal/source" 26 29 ) 27 30 28 31 // Option specifies a parse option. 29 - type Option func(p *parser) 32 + type Option interface { 33 + apply(cfg *Config) 34 + } 35 + 36 + var _ Option = Config{} 37 + 38 + // Config represents the end result of applying a set of options. 39 + // The zero value is not OK to use: use [NewConfig] to construct 40 + // a Config value before using it. 41 + // 42 + // Config itself implements [Option] by overwriting the 43 + // entire configuration. 44 + // 45 + // Config is comparable. 46 + type Config struct { 47 + // valid is set by NewConfig and is used to check 48 + // that a Config has been created correctly. 49 + valid bool 50 + 51 + // Mode holds a bitmask of boolean parser options. 52 + Mode Mode 53 + 54 + // Version holds the language version to use when 55 + // parsing the CUE syntax. 56 + Version string 57 + } 58 + 59 + // Ensure that Config is comparable. 60 + var _ = Config{} == Config{} 61 + 62 + // apply implements [Option] 63 + func (cfg Config) apply(cfg1 *Config) { 64 + if !cfg.valid { 65 + panic("zero parser.Config value used; use parser.NewConfig!") 66 + } 67 + *cfg1 = cfg 68 + } 69 + 70 + // NewConfig returns the configuration containing all default values 71 + // with the given options applied. 72 + func NewConfig(opts ...Option) Config { 73 + return Config{ 74 + valid: true, 75 + Version: cueversion.LanguageVersion(), 76 + }.Apply(opts...) 77 + } 30 78 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 79 + // Apply applies all the given options to cfg and 80 + // returns the resulting configuration. 81 + func (cfg Config) Apply(opts ...Option) Config { 82 + for _, opt := range opts { 83 + opt.apply(&cfg) 36 84 } 85 + return cfg 86 + } 87 + 88 + // optionFunc implements [Option] for a function. 89 + type optionFunc func(cfg *Config) 90 + 91 + func (f optionFunc) apply(cfg *Config) { 92 + f(cfg) 93 + } 94 + 95 + // A Mode value is a set of flags (or 0). 96 + // It controls the amount of source code parsed and other optional 97 + // parser functionality. 98 + // 99 + // Mode implements [Option] by or-ing all its bits 100 + // with [Config.Mode]. 101 + type Mode uint 102 + 103 + const ( 104 + // PackageClauseOnly causes parsing to stop after the package clause. 105 + PackageClauseOnly Mode = 1 << iota 37 106 38 107 // ImportsOnly causes parsing to stop parsing after the import declarations. 39 - ImportsOnly Option = importsOnly 40 - importsOnly = func(p *parser) { 41 - p.mode |= importsOnlyMode 42 - } 108 + ImportsOnly 43 109 44 110 // ParseComments causes comments to be parsed. 45 - ParseComments Option = parseComments 46 - parseComments = func(p *parser) { 47 - p.mode |= parseCommentsMode 48 - } 111 + ParseComments 49 112 50 113 // ParseFuncs causes function declarations to be parsed. 51 114 // 52 115 // This is an experimental function and the API is likely to 53 116 // change or dissapear. 54 - ParseFuncs Option = parseFuncs 55 - parseFuncs = func(p *parser) { 56 - p.mode |= parseFuncsMode 57 - } 117 + ParseFuncs 58 118 59 119 // Trace causes parsing to print a trace of parsed productions. 60 - Trace Option = traceOpt 61 - traceOpt = func(p *parser) { 62 - p.mode |= traceMode 63 - } 120 + Trace 64 121 65 122 // DeclarationErrors causes parsing to report declaration errors. 66 - DeclarationErrors Option = declarationErrors 67 - declarationErrors = func(p *parser) { 68 - p.mode |= declarationErrorsMode 69 - } 123 + DeclarationErrors 70 124 71 125 // 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 - } 126 + AllErrors 76 127 77 128 // AllowPartial allows the parser to be used on a prefix buffer. 78 - AllowPartial Option = allowPartial 79 - allowPartial = func(p *parser) { 80 - p.mode |= partialMode 129 + AllowPartial 130 + ) 131 + 132 + // apply implements [Option]. 133 + func (m Mode) apply(c *Config) { 134 + c.Mode |= m 135 + } 136 + 137 + // Version specifies the language version to use when parsing 138 + // the CUE. The argument must be a valid semantic version, as 139 + // checked by [semver.IsValid]. 140 + func Version(v string) Option { 141 + if !semver.IsValid(v) { 142 + panic(fmt.Errorf("invalid language version %q", v)) 81 143 } 82 - ) 144 + return optionFunc(func(c *Config) { 145 + c.Version = v 146 + }) 147 + } 83 148 84 149 // FromVersion specifies until which legacy version the parser should provide 85 150 // backwards compatibility. 151 + // Deprecated: use [Version] instead. 86 152 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 } 153 + return optionFunc(func(cfg *Config) {}) 94 154 } 95 155 96 156 // DeprecationError is a sentinel error to indicate that an error is ··· 104 164 } 105 165 106 166 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 167 + // Deprecated: see [Version]. 168 + Latest = 0 116 169 117 - fullCompatibility = -1000 170 + // Deprecated: see [Version]. 171 + FullBackwardCompatibility = 0 118 172 ) 119 173 120 174 // FileOffset specifies the File position info to use. 121 175 // 122 176 // Deprecated: this has no effect. 123 177 func FileOffset(pos int) Option { 124 - return func(p *parser) {} 178 + return optionFunc(func(*Config) {}) 125 179 } 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 180 143 181 // ParseFile parses the source code of a single CUE source file and returns 144 182 // the corresponding File node. The source code may be provided via ··· 228 266 if p.tok == token.COMMA && p.lit == "\n" { 229 267 p.next() 230 268 } 231 - if p.mode&partialMode == 0 { 269 + if p.cfg.Mode&AllowPartial == 0 { 232 270 p.expect(token.EOF) 233 271 } 234 272
+11 -13
cue/parser/parser.go
··· 34 34 scanner scanner.Scanner 35 35 36 36 // Tracing/debugging 37 - mode mode // parsing mode 38 - trace bool // == (mode & Trace != 0) 37 + cfg Config 38 + trace bool // == (cfg.Mode & Trace != 0) 39 39 panicking bool // set if we are bailing out due to too many errors. 40 40 indent int // indentation used for tracing output 41 41 ··· 72 72 version int 73 73 } 74 74 75 - func (p *parser) init(filename string, src []byte, mode []Option) { 76 - for _, f := range mode { 77 - f(p) 78 - } 75 + func (p *parser) init(filename string, src []byte, opts []Option) { 76 + p.cfg = NewConfig().Apply(opts...) 79 77 p.file = token.NewFile(filename, -1, len(src)) 80 78 81 79 var m scanner.Mode 82 - if p.mode&parseCommentsMode != 0 { 80 + if p.cfg.Mode&ParseComments != 0 { 83 81 m = scanner.ScanComments 84 82 } 85 83 eh := func(pos token.Pos, msg string, args []interface{}) { ··· 87 85 } 88 86 p.scanner.Init(p.file, src, eh, m) 89 87 90 - p.trace = p.mode&traceMode != 0 // for convenience (p.trace is used frequently) 88 + p.trace = p.cfg.Mode&Trace != 0 // for convenience (p.trace is used frequently) 91 89 92 90 p.comments = &commentState{pos: -1} 93 91 ··· 422 420 // If AllErrors is not set, discard errors reported on the same line 423 421 // as the last recorded error and stop parsing if there are more than 424 422 // 10 errors. 425 - if p.mode&allErrorsMode == 0 { 423 + if p.cfg.Mode&AllErrors == 0 { 426 424 errors := errors.Errors(p.errors) 427 425 n := len(errors) 428 426 if n > 0 && errors[n-1].Position().Line() == ePos.Line() { ··· 608 606 return p.parseList() 609 607 610 608 case token.FUNC: 611 - if p.mode&parseFuncsMode != 0 { 609 + if p.cfg.Mode&ParseFuncs != 0 { 612 610 return p.parseFunc() 613 611 } else { 614 612 return p.parseKeyIdent() ··· 1719 1717 var name *ast.Ident 1720 1718 p.expect(token.IDENT) 1721 1719 name = p.parseIdent() 1722 - if name.Name == "_" && p.mode&declarationErrorsMode != 0 { 1720 + if name.Name == "_" && p.cfg.Mode&DeclarationErrors != 0 { 1723 1721 p.errf(p.pos, "invalid package name _") 1724 1722 } 1725 1723 if isDefinition(name) { ··· 1739 1737 p.consumeDeclComma() 1740 1738 } 1741 1739 1742 - if p.mode&packageClauseOnlyMode == 0 { 1740 + if p.cfg.Mode&PackageClauseOnly == 0 { 1743 1741 // import decls 1744 1742 for p.tok == token.IDENT && p.lit == "import" { 1745 1743 decls = append(decls, p.parseImports()) 1746 1744 } 1747 1745 1748 - if p.mode&importsOnlyMode == 0 { 1746 + if p.cfg.Mode&ImportsOnly == 0 { 1749 1747 // rest of package decls 1750 1748 // TODO: loop and allow multiple expressions. 1751 1749 decls = append(decls, p.parseFieldList()...)