mirror of
https://github.com/golang/go.git
synced 2026-06-27 03:11:23 +00:00
cmd/dist: use go/build/constraint to parse build constraints
Back when this simple implementation was added (in CL 359314), the required bootstrap was a very old version of Go. By now, Go 1.27 requires bootstrap of Go 1.24.6 or later, so it can rely on the go/build/constraint package being available. This fixes a bug affecting the precedence of || and && that the previous implementation had, otherwise the only change is error text. For #41184. Fixes #79185. Change-Id: I8b520629a702e9d6ddf7672ef3893f237569e93e Reviewed-on: https://go-review.googlesource.com/c/go/+/773822 Auto-Submit: Dmitri Shuralyov <dmitshur@golang.org> LUCI-TryBot-Result: golang-scoped@luci-project-accounts.iam.gserviceaccount.com <golang-scoped@luci-project-accounts.iam.gserviceaccount.com> Reviewed-by: Dmitri Shuralyov <dmitshur@google.com> Reviewed-by: Alan Donovan <adonovan@google.com>
This commit is contained in:
parent
ba02236208
commit
e1dff0e0b9
2 changed files with 11 additions and 125 deletions
126
src/cmd/dist/buildtag.go
vendored
126
src/cmd/dist/buildtag.go
vendored
|
|
@ -6,128 +6,14 @@ package main
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"go/build/constraint"
|
||||
)
|
||||
|
||||
// exprParser is a //go:build expression parser and evaluator.
|
||||
// The parser is a trivial precedence-based parser which is still
|
||||
// almost overkill for these very simple expressions.
|
||||
type exprParser struct {
|
||||
x string
|
||||
t exprToken // upcoming token
|
||||
}
|
||||
|
||||
// val is the value type result of parsing.
|
||||
// We don't keep a parse tree, just the value of the expression.
|
||||
type val bool
|
||||
|
||||
// exprToken describes a single token in the input.
|
||||
// Prefix operators define a prefix func that parses the
|
||||
// upcoming value. Binary operators define an infix func
|
||||
// that combines two values according to the operator.
|
||||
// In that case, the parsing loop parses the two values.
|
||||
type exprToken struct {
|
||||
tok string
|
||||
prec int
|
||||
prefix func(*exprParser) val
|
||||
infix func(val, val) val
|
||||
}
|
||||
|
||||
var exprTokens []exprToken
|
||||
|
||||
func init() { // init to break init cycle
|
||||
exprTokens = []exprToken{
|
||||
{tok: "&&", prec: 1, infix: func(x, y val) val { return x && y }},
|
||||
{tok: "||", prec: 2, infix: func(x, y val) val { return x || y }},
|
||||
{tok: "!", prec: 3, prefix: (*exprParser).not},
|
||||
{tok: "(", prec: 3, prefix: (*exprParser).paren},
|
||||
{tok: ")"},
|
||||
}
|
||||
}
|
||||
|
||||
// matchexpr parses and evaluates the //go:build expression x.
|
||||
func matchexpr(x string) (matched bool, err error) {
|
||||
defer func() {
|
||||
if e := recover(); e != nil {
|
||||
matched = false
|
||||
err = fmt.Errorf("parsing //go:build line: %v", e)
|
||||
}
|
||||
}()
|
||||
|
||||
p := &exprParser{x: x}
|
||||
p.next()
|
||||
v := p.parse(0)
|
||||
if p.t.tok != "end of expression" {
|
||||
panic("unexpected " + p.t.tok)
|
||||
func matchexpr(x string) (matched bool, _ error) {
|
||||
c, err := constraint.Parse("//go:build " + x)
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("parsing //go:build line: %v", err)
|
||||
}
|
||||
return bool(v), nil
|
||||
}
|
||||
|
||||
// parse parses an expression, including binary operators at precedence >= prec.
|
||||
func (p *exprParser) parse(prec int) val {
|
||||
if p.t.prefix == nil {
|
||||
panic("unexpected " + p.t.tok)
|
||||
}
|
||||
v := p.t.prefix(p)
|
||||
for p.t.prec >= prec && p.t.infix != nil {
|
||||
t := p.t
|
||||
p.next()
|
||||
v = t.infix(v, p.parse(t.prec+1))
|
||||
}
|
||||
return v
|
||||
}
|
||||
|
||||
// not is the prefix parser for a ! token.
|
||||
func (p *exprParser) not() val {
|
||||
p.next()
|
||||
return !p.parse(100)
|
||||
}
|
||||
|
||||
// paren is the prefix parser for a ( token.
|
||||
func (p *exprParser) paren() val {
|
||||
p.next()
|
||||
v := p.parse(0)
|
||||
if p.t.tok != ")" {
|
||||
panic("missing )")
|
||||
}
|
||||
p.next()
|
||||
return v
|
||||
}
|
||||
|
||||
// next advances the parser to the next token,
|
||||
// leaving the token in p.t.
|
||||
func (p *exprParser) next() {
|
||||
p.x = strings.TrimSpace(p.x)
|
||||
if p.x == "" {
|
||||
p.t = exprToken{tok: "end of expression"}
|
||||
return
|
||||
}
|
||||
for _, t := range exprTokens {
|
||||
if strings.HasPrefix(p.x, t.tok) {
|
||||
p.x = p.x[len(t.tok):]
|
||||
p.t = t
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
i := 0
|
||||
for i < len(p.x) && validtag(p.x[i]) {
|
||||
i++
|
||||
}
|
||||
if i == 0 {
|
||||
panic(fmt.Sprintf("syntax error near %#q", rune(p.x[i])))
|
||||
}
|
||||
tag := p.x[:i]
|
||||
p.x = p.x[i:]
|
||||
p.t = exprToken{
|
||||
tok: "tag",
|
||||
prefix: func(p *exprParser) val {
|
||||
p.next()
|
||||
return val(matchtag(tag))
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func validtag(c byte) bool {
|
||||
return 'A' <= c && c <= 'Z' || 'a' <= c && c <= 'z' || '0' <= c && c <= '9' || c == '.' || c == '_'
|
||||
return c.Eval(matchtag), nil
|
||||
}
|
||||
|
|
|
|||
10
src/cmd/dist/buildtag_test.go
vendored
10
src/cmd/dist/buildtag_test.go
vendored
|
|
@ -22,16 +22,16 @@ var buildParserTests = []struct {
|
|||
{"gc || gccgo", true, nil},
|
||||
{"gc || (gccgo && !gccgo)", true, nil},
|
||||
{"gc && (gccgo || !gccgo)", true, nil},
|
||||
{"gccgo && gc || gc", false, nil}, // TODO: should be true
|
||||
{"gccgo && gc || gc", true, nil},
|
||||
{"!(gc && (gccgo || !gccgo))", false, nil},
|
||||
{"gccgo || gc", true, nil},
|
||||
{"!(!(!(gccgo || gc)))", false, nil},
|
||||
{"compiler_bootstrap", false, nil},
|
||||
{"cmd_go_bootstrap", true, nil},
|
||||
{"syntax(error", false, fmt.Errorf("parsing //go:build line: unexpected (")},
|
||||
{"(gc", false, fmt.Errorf("parsing //go:build line: missing )")},
|
||||
{"gc gc", false, fmt.Errorf("parsing //go:build line: unexpected tag")},
|
||||
{"(gc))", false, fmt.Errorf("parsing //go:build line: unexpected )")},
|
||||
{"syntax(error", false, fmt.Errorf("parsing //go:build line: unexpected token (")},
|
||||
{"(gc", false, fmt.Errorf("parsing //go:build line: missing close paren")},
|
||||
{"gc gc", false, fmt.Errorf("parsing //go:build line: unexpected token gc")},
|
||||
{"(gc))", false, fmt.Errorf("parsing //go:build line: unexpected token )")},
|
||||
}
|
||||
|
||||
func TestBuildParser(t *testing.T) {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue