Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
wa-lang
wa
提交
6fb188c2
wa
项目概览
wa-lang
/
wa
12 个月 前同步成功
通知
68
Star
655
Fork
45
代码
文件
提交
分支
Tags
贡献者
分支图
Diff
Issue
0
列表
看板
标记
里程碑
合并请求
0
DevOps
流水线
流水线任务
计划
Wiki
0
Wiki
分析
仓库
DevOps
项目成员
Pages
wa
项目概览
项目概览
详情
发布
仓库
仓库
文件
提交
分支
标签
贡献者
分支图
比较
Issue
0
Issue
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
Pages
DevOps
DevOps
流水线
流水线任务
计划
分析
分析
仓库分析
DevOps
Wiki
0
Wiki
成员
成员
收起侧边栏
关闭侧边栏
动态
分支图
创建新Issue
流水线任务
提交
Issue看板
体验新版 GitCode,发现更多精彩内容 >>
提交
6fb188c2
编写于
3月 02, 2023
作者:
chai2010
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
增加 buildtag 包
上级
f2ba3dfa
变更
2
显示空白变更内容
内联
并排
Showing
2 changed file
with
687 addition
and
0 deletion
+687
-0
internal/loader/buildtag/expr.go
internal/loader/buildtag/expr.go
+417
-0
internal/loader/buildtag/expr_test.go
internal/loader/buildtag/expr_test.go
+270
-0
未找到文件。
internal/loader/buildtag/expr.go
0 → 100644
浏览文件 @
6fb188c2
// Copyright 2020 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package
buildtag
import
(
"errors"
"strings"
"unicode"
"unicode/utf8"
)
// An Expr is a build tag constraint expression.
// The underlying concrete type is *AndExpr, *OrExpr, *NotExpr, or *TagExpr.
type
Expr
interface
{
// String returns the string form of the expression,
// using the boolean syntax used in #wa:build lines.
String
()
string
// Eval reports whether the expression evaluates to true.
// It calls ok(tag) as needed to find out whether a given build tag
// is satisfied by the current build configuration.
Eval
(
ok
func
(
tag
string
)
bool
)
bool
// The presence of an isExpr method explicitly marks the type as an Expr.
// Only implementations in this package should be used as Exprs.
isExpr
()
}
// A TagExpr is an Expr for the single tag Tag.
type
TagExpr
struct
{
Tag
string
// for example, “linux” or “cgo”
}
func
(
x
*
TagExpr
)
isExpr
()
{}
func
(
x
*
TagExpr
)
Eval
(
ok
func
(
tag
string
)
bool
)
bool
{
return
ok
(
x
.
Tag
)
}
func
(
x
*
TagExpr
)
String
()
string
{
return
x
.
Tag
}
func
tag
(
tag
string
)
Expr
{
return
&
TagExpr
{
tag
}
}
// A NotExpr represents the expression !X (the negation of X).
type
NotExpr
struct
{
X
Expr
}
func
(
x
*
NotExpr
)
isExpr
()
{}
func
(
x
*
NotExpr
)
Eval
(
ok
func
(
tag
string
)
bool
)
bool
{
return
!
x
.
X
.
Eval
(
ok
)
}
func
(
x
*
NotExpr
)
String
()
string
{
s
:=
x
.
X
.
String
()
switch
x
.
X
.
(
type
)
{
case
*
AndExpr
,
*
OrExpr
:
s
=
"("
+
s
+
")"
}
return
"!"
+
s
}
func
not
(
x
Expr
)
Expr
{
return
&
NotExpr
{
x
}
}
// An AndExpr represents the expression X && Y.
type
AndExpr
struct
{
X
,
Y
Expr
}
func
(
x
*
AndExpr
)
isExpr
()
{}
func
(
x
*
AndExpr
)
Eval
(
ok
func
(
tag
string
)
bool
)
bool
{
// Note: Eval both, to make sure ok func observes all tags.
xok
:=
x
.
X
.
Eval
(
ok
)
yok
:=
x
.
Y
.
Eval
(
ok
)
return
xok
&&
yok
}
func
(
x
*
AndExpr
)
String
()
string
{
return
andArg
(
x
.
X
)
+
" && "
+
andArg
(
x
.
Y
)
}
func
andArg
(
x
Expr
)
string
{
s
:=
x
.
String
()
if
_
,
ok
:=
x
.
(
*
OrExpr
);
ok
{
s
=
"("
+
s
+
")"
}
return
s
}
func
and
(
x
,
y
Expr
)
Expr
{
return
&
AndExpr
{
x
,
y
}
}
// An OrExpr represents the expression X || Y.
type
OrExpr
struct
{
X
,
Y
Expr
}
func
(
x
*
OrExpr
)
isExpr
()
{}
func
(
x
*
OrExpr
)
Eval
(
ok
func
(
tag
string
)
bool
)
bool
{
// Note: Eval both, to make sure ok func observes all tags.
xok
:=
x
.
X
.
Eval
(
ok
)
yok
:=
x
.
Y
.
Eval
(
ok
)
return
xok
||
yok
}
func
(
x
*
OrExpr
)
String
()
string
{
return
orArg
(
x
.
X
)
+
" || "
+
orArg
(
x
.
Y
)
}
func
orArg
(
x
Expr
)
string
{
s
:=
x
.
String
()
if
_
,
ok
:=
x
.
(
*
AndExpr
);
ok
{
s
=
"("
+
s
+
")"
}
return
s
}
func
or
(
x
,
y
Expr
)
Expr
{
return
&
OrExpr
{
x
,
y
}
}
// A SyntaxError reports a syntax error in a parsed build expression.
type
SyntaxError
struct
{
Offset
int
// byte offset in input where error was detected
Err
string
// description of error
}
func
(
e
*
SyntaxError
)
Error
()
string
{
return
e
.
Err
}
var
errNotConstraint
=
errors
.
New
(
"not a build constraint"
)
// Parse parses a single build constraint line of the form “#wa:build ...”
// and returns the corresponding boolean expression.
func
Parse
(
line
string
)
(
Expr
,
error
)
{
if
text
,
ok
:=
splitWaBuild
(
line
);
ok
{
return
parseExpr
(
text
)
}
return
nil
,
errNotConstraint
}
// IsWaBuild reports whether the line of text is a “#wa:build” constraint.
// It only checks the prefix of the text, not that the expression itself parses.
func
IsWaBuild
(
line
string
)
bool
{
_
,
ok
:=
splitWaBuild
(
line
)
return
ok
}
// splitWaBuild splits apart the leading #wa:build prefix in line from the build expression itself.
// It returns "", false if the input is not a #wa:build line or if the input contains multiple lines.
func
splitWaBuild
(
line
string
)
(
expr
string
,
ok
bool
)
{
// A single trailing newline is OK; otherwise multiple lines are not.
if
len
(
line
)
>
0
&&
line
[
len
(
line
)
-
1
]
==
'\n'
{
line
=
line
[
:
len
(
line
)
-
1
]
}
if
strings
.
Contains
(
line
,
"
\n
"
)
{
return
""
,
false
}
if
!
strings
.
HasPrefix
(
line
,
"#wa:build"
)
{
return
""
,
false
}
line
=
strings
.
TrimSpace
(
line
)
line
=
line
[
len
(
"#wa:build"
)
:
]
// If strings.TrimSpace finds more to trim after removing the #wa:build prefix,
// it means that the prefix was followed by a space, making this a #wa:build line
// (as opposed to a #wa:buildsomethingelse line).
// If line is empty, we had "#wa:build" by itself, which also counts.
trim
:=
strings
.
TrimSpace
(
line
)
if
len
(
line
)
==
len
(
trim
)
&&
line
!=
""
{
return
""
,
false
}
return
trim
,
true
}
// An exprParser holds state for parsing a build expression.
type
exprParser
struct
{
s
string
// input string
i
int
// next read location in s
tok
string
// last token read
isTag
bool
pos
int
// position (start) of last token
}
// parseExpr parses a boolean build tag expression.
func
parseExpr
(
text
string
)
(
x
Expr
,
err
error
)
{
defer
func
()
{
if
e
:=
recover
();
e
!=
nil
{
if
e
,
ok
:=
e
.
(
*
SyntaxError
);
ok
{
err
=
e
return
}
panic
(
e
)
// unreachable unless parser has a bug
}
}()
p
:=
&
exprParser
{
s
:
text
}
x
=
p
.
or
()
if
p
.
tok
!=
""
{
panic
(
&
SyntaxError
{
Offset
:
p
.
pos
,
Err
:
"unexpected token "
+
p
.
tok
})
}
return
x
,
nil
}
// or parses a sequence of || expressions.
// On entry, the next input token has not yet been lexed.
// On exit, the next input token has been lexed and is in p.tok.
func
(
p
*
exprParser
)
or
()
Expr
{
x
:=
p
.
and
()
for
p
.
tok
==
"||"
{
x
=
or
(
x
,
p
.
and
())
}
return
x
}
// and parses a sequence of && expressions.
// On entry, the next input token has not yet been lexed.
// On exit, the next input token has been lexed and is in p.tok.
func
(
p
*
exprParser
)
and
()
Expr
{
x
:=
p
.
not
()
for
p
.
tok
==
"&&"
{
x
=
and
(
x
,
p
.
not
())
}
return
x
}
// not parses a ! expression.
// On entry, the next input token has not yet been lexed.
// On exit, the next input token has been lexed and is in p.tok.
func
(
p
*
exprParser
)
not
()
Expr
{
p
.
lex
()
if
p
.
tok
==
"!"
{
p
.
lex
()
if
p
.
tok
==
"!"
{
panic
(
&
SyntaxError
{
Offset
:
p
.
pos
,
Err
:
"double negation not allowed"
})
}
return
not
(
p
.
atom
())
}
return
p
.
atom
()
}
// atom parses a tag or a parenthesized expression.
// On entry, the next input token HAS been lexed.
// On exit, the next input token has been lexed and is in p.tok.
func
(
p
*
exprParser
)
atom
()
Expr
{
// first token already in p.tok
if
p
.
tok
==
"("
{
pos
:=
p
.
pos
defer
func
()
{
if
e
:=
recover
();
e
!=
nil
{
if
e
,
ok
:=
e
.
(
*
SyntaxError
);
ok
&&
e
.
Err
==
"unexpected end of expression"
{
e
.
Err
=
"missing close paren"
}
panic
(
e
)
}
}()
x
:=
p
.
or
()
if
p
.
tok
!=
")"
{
panic
(
&
SyntaxError
{
Offset
:
pos
,
Err
:
"missing close paren"
})
}
p
.
lex
()
return
x
}
if
!
p
.
isTag
{
if
p
.
tok
==
""
{
panic
(
&
SyntaxError
{
Offset
:
p
.
pos
,
Err
:
"unexpected end of expression"
})
}
panic
(
&
SyntaxError
{
Offset
:
p
.
pos
,
Err
:
"unexpected token "
+
p
.
tok
})
}
tok
:=
p
.
tok
p
.
lex
()
return
tag
(
tok
)
}
// lex finds and consumes the next token in the input stream.
// On return, p.tok is set to the token text,
// p.isTag reports whether the token was a tag,
// and p.pos records the byte offset of the start of the token in the input stream.
// If lex reaches the end of the input, p.tok is set to the empty string.
// For any other syntax error, lex panics with a SyntaxError.
func
(
p
*
exprParser
)
lex
()
{
p
.
isTag
=
false
for
p
.
i
<
len
(
p
.
s
)
&&
(
p
.
s
[
p
.
i
]
==
' '
||
p
.
s
[
p
.
i
]
==
'\t'
)
{
p
.
i
++
}
if
p
.
i
>=
len
(
p
.
s
)
{
p
.
tok
=
""
p
.
pos
=
p
.
i
return
}
switch
p
.
s
[
p
.
i
]
{
case
'('
,
')'
,
'!'
:
p
.
pos
=
p
.
i
p
.
i
++
p
.
tok
=
p
.
s
[
p
.
pos
:
p
.
i
]
return
case
'&'
,
'|'
:
if
p
.
i
+
1
>=
len
(
p
.
s
)
||
p
.
s
[
p
.
i
+
1
]
!=
p
.
s
[
p
.
i
]
{
panic
(
&
SyntaxError
{
Offset
:
p
.
i
,
Err
:
"invalid syntax at "
+
string
(
rune
(
p
.
s
[
p
.
i
]))})
}
p
.
pos
=
p
.
i
p
.
i
+=
2
p
.
tok
=
p
.
s
[
p
.
pos
:
p
.
i
]
return
}
tag
:=
p
.
s
[
p
.
i
:
]
for
i
,
c
:=
range
tag
{
if
!
unicode
.
IsLetter
(
c
)
&&
!
unicode
.
IsDigit
(
c
)
&&
c
!=
'_'
&&
c
!=
'.'
{
tag
=
tag
[
:
i
]
break
}
}
if
tag
==
""
{
c
,
_
:=
utf8
.
DecodeRuneInString
(
p
.
s
[
p
.
i
:
])
panic
(
&
SyntaxError
{
Offset
:
p
.
i
,
Err
:
"invalid syntax at "
+
string
(
c
)})
}
p
.
pos
=
p
.
i
p
.
i
+=
len
(
tag
)
p
.
tok
=
p
.
s
[
p
.
pos
:
p
.
i
]
p
.
isTag
=
true
return
}
// isValidTag reports whether the word is a valid build tag.
// Tags must be letters, digits, underscores or dots.
// Unlike in Go identifiers, all digits are fine (e.g., "386").
func
isValidTag
(
word
string
)
bool
{
if
word
==
""
{
return
false
}
for
_
,
c
:=
range
word
{
if
!
unicode
.
IsLetter
(
c
)
&&
!
unicode
.
IsDigit
(
c
)
&&
c
!=
'_'
&&
c
!=
'.'
{
return
false
}
}
return
true
}
// pushNot applies DeMorgan's law to push negations down the expression,
// so that only tags are negated in the result.
// (It applies the rewrites !(X && Y) => (!X || !Y) and !(X || Y) => (!X && !Y).)
func
pushNot
(
x
Expr
,
not
bool
)
Expr
{
switch
x
:=
x
.
(
type
)
{
default
:
// unreachable
return
x
case
*
NotExpr
:
if
_
,
ok
:=
x
.
X
.
(
*
TagExpr
);
ok
&&
!
not
{
return
x
}
return
pushNot
(
x
.
X
,
!
not
)
case
*
TagExpr
:
if
not
{
return
&
NotExpr
{
X
:
x
}
}
return
x
case
*
AndExpr
:
x1
:=
pushNot
(
x
.
X
,
not
)
y1
:=
pushNot
(
x
.
Y
,
not
)
if
not
{
return
or
(
x1
,
y1
)
}
if
x1
==
x
.
X
&&
y1
==
x
.
Y
{
return
x
}
return
and
(
x1
,
y1
)
case
*
OrExpr
:
x1
:=
pushNot
(
x
.
X
,
not
)
y1
:=
pushNot
(
x
.
Y
,
not
)
if
not
{
return
and
(
x1
,
y1
)
}
if
x1
==
x
.
X
&&
y1
==
x
.
Y
{
return
x
}
return
or
(
x1
,
y1
)
}
}
// appendSplitAnd appends x to list while splitting apart any top-level && expressions.
// For example, appendSplitAnd({W}, X && Y && Z) = {W, X, Y, Z}.
func
appendSplitAnd
(
list
[]
Expr
,
x
Expr
)
[]
Expr
{
if
x
,
ok
:=
x
.
(
*
AndExpr
);
ok
{
list
=
appendSplitAnd
(
list
,
x
.
X
)
list
=
appendSplitAnd
(
list
,
x
.
Y
)
return
list
}
return
append
(
list
,
x
)
}
// appendSplitOr appends x to list while splitting apart any top-level || expressions.
// For example, appendSplitOr({W}, X || Y || Z) = {W, X, Y, Z}.
func
appendSplitOr
(
list
[]
Expr
,
x
Expr
)
[]
Expr
{
if
x
,
ok
:=
x
.
(
*
OrExpr
);
ok
{
list
=
appendSplitOr
(
list
,
x
.
X
)
list
=
appendSplitOr
(
list
,
x
.
Y
)
return
list
}
return
append
(
list
,
x
)
}
internal/loader/buildtag/expr_test.go
0 → 100644
浏览文件 @
6fb188c2
// Copyright 2020 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package
buildtag
import
(
"fmt"
"reflect"
"strings"
"testing"
)
var
exprStringTests
=
[]
struct
{
x
Expr
out
string
}{
{
x
:
tag
(
"abc"
),
out
:
"abc"
,
},
{
x
:
not
(
tag
(
"abc"
)),
out
:
"!abc"
,
},
{
x
:
not
(
and
(
tag
(
"abc"
),
tag
(
"def"
))),
out
:
"!(abc && def)"
,
},
{
x
:
and
(
tag
(
"abc"
),
or
(
tag
(
"def"
),
tag
(
"ghi"
))),
out
:
"abc && (def || ghi)"
,
},
{
x
:
or
(
and
(
tag
(
"abc"
),
tag
(
"def"
)),
tag
(
"ghi"
)),
out
:
"(abc && def) || ghi"
,
},
}
func
TestExprString
(
t
*
testing
.
T
)
{
for
i
,
tt
:=
range
exprStringTests
{
t
.
Run
(
fmt
.
Sprint
(
i
),
func
(
t
*
testing
.
T
)
{
s
:=
tt
.
x
.
String
()
if
s
!=
tt
.
out
{
t
.
Errorf
(
"String() mismatch:
\n
have %s
\n
want %s"
,
s
,
tt
.
out
)
}
})
}
}
var
lexTests
=
[]
struct
{
in
string
out
string
}{
{
""
,
""
},
{
"x"
,
"x"
},
{
"x.y"
,
"x.y"
},
{
"x_y"
,
"x_y"
},
{
"αx"
,
"αx"
},
{
"αx²"
,
"αx err: invalid syntax at ²"
},
{
"go1.2"
,
"go1.2"
},
{
"x y"
,
"x y"
},
{
"x!y"
,
"x ! y"
},
{
"&&||!()xy yx "
,
"&& || ! ( ) xy yx"
},
{
"x~"
,
"x err: invalid syntax at ~"
},
{
"x ~"
,
"x err: invalid syntax at ~"
},
{
"x &"
,
"x err: invalid syntax at &"
},
{
"x &y"
,
"x err: invalid syntax at &"
},
}
func
TestLex
(
t
*
testing
.
T
)
{
for
i
,
tt
:=
range
lexTests
{
t
.
Run
(
fmt
.
Sprint
(
i
),
func
(
t
*
testing
.
T
)
{
p
:=
&
exprParser
{
s
:
tt
.
in
}
out
:=
""
for
{
tok
,
err
:=
lexHelp
(
p
)
if
tok
==
""
&&
err
==
nil
{
break
}
if
out
!=
""
{
out
+=
" "
}
if
err
!=
nil
{
out
+=
"err: "
+
err
.
Error
()
break
}
out
+=
tok
}
if
out
!=
tt
.
out
{
t
.
Errorf
(
"lex(%q):
\n
have %s
\n
want %s"
,
tt
.
in
,
out
,
tt
.
out
)
}
})
}
}
func
lexHelp
(
p
*
exprParser
)
(
tok
string
,
err
error
)
{
defer
func
()
{
if
e
:=
recover
();
e
!=
nil
{
if
e
,
ok
:=
e
.
(
*
SyntaxError
);
ok
{
err
=
e
return
}
panic
(
e
)
}
}()
p
.
lex
()
return
p
.
tok
,
nil
}
var
parseExprTests
=
[]
struct
{
in
string
x
Expr
}{
{
"x"
,
tag
(
"x"
)},
{
"x&&y"
,
and
(
tag
(
"x"
),
tag
(
"y"
))},
{
"x||y"
,
or
(
tag
(
"x"
),
tag
(
"y"
))},
{
"(x)"
,
tag
(
"x"
)},
{
"x||y&&z"
,
or
(
tag
(
"x"
),
and
(
tag
(
"y"
),
tag
(
"z"
)))},
{
"x&&y||z"
,
or
(
and
(
tag
(
"x"
),
tag
(
"y"
)),
tag
(
"z"
))},
{
"x&&(y||z)"
,
and
(
tag
(
"x"
),
or
(
tag
(
"y"
),
tag
(
"z"
)))},
{
"(x||y)&&z"
,
and
(
or
(
tag
(
"x"
),
tag
(
"y"
)),
tag
(
"z"
))},
{
"!(x&&y)"
,
not
(
and
(
tag
(
"x"
),
tag
(
"y"
)))},
}
func
TestParseExpr
(
t
*
testing
.
T
)
{
for
i
,
tt
:=
range
parseExprTests
{
t
.
Run
(
fmt
.
Sprint
(
i
),
func
(
t
*
testing
.
T
)
{
x
,
err
:=
parseExpr
(
tt
.
in
)
if
err
!=
nil
{
t
.
Fatal
(
err
)
}
if
x
.
String
()
!=
tt
.
x
.
String
()
{
t
.
Errorf
(
"parseExpr(%q):
\n
have %s
\n
want %s"
,
tt
.
in
,
x
,
tt
.
x
)
}
})
}
}
var
parseExprErrorTests
=
[]
struct
{
in
string
err
error
}{
{
"x && "
,
&
SyntaxError
{
Offset
:
5
,
Err
:
"unexpected end of expression"
}},
{
"x && ("
,
&
SyntaxError
{
Offset
:
6
,
Err
:
"missing close paren"
}},
{
"x && ||"
,
&
SyntaxError
{
Offset
:
5
,
Err
:
"unexpected token ||"
}},
{
"x && !"
,
&
SyntaxError
{
Offset
:
6
,
Err
:
"unexpected end of expression"
}},
{
"x && !!"
,
&
SyntaxError
{
Offset
:
6
,
Err
:
"double negation not allowed"
}},
{
"x !"
,
&
SyntaxError
{
Offset
:
2
,
Err
:
"unexpected token !"
}},
{
"x && (y"
,
&
SyntaxError
{
Offset
:
5
,
Err
:
"missing close paren"
}},
}
func
TestParseError
(
t
*
testing
.
T
)
{
for
i
,
tt
:=
range
parseExprErrorTests
{
t
.
Run
(
fmt
.
Sprint
(
i
),
func
(
t
*
testing
.
T
)
{
x
,
err
:=
parseExpr
(
tt
.
in
)
if
err
==
nil
{
t
.
Fatalf
(
"parseExpr(%q) = %v, want error"
,
tt
.
in
,
x
)
}
if
!
reflect
.
DeepEqual
(
err
,
tt
.
err
)
{
t
.
Fatalf
(
"parseExpr(%q): wrong error:
\n
have %#v
\n
want %#v"
,
tt
.
in
,
err
,
tt
.
err
)
}
})
}
}
var
exprEvalTests
=
[]
struct
{
in
string
ok
bool
tags
string
}{
{
"x"
,
false
,
"x"
},
{
"x && y"
,
false
,
"x y"
},
{
"x || y"
,
false
,
"x y"
},
{
"!x && yes"
,
true
,
"x yes"
},
{
"yes || y"
,
true
,
"y yes"
},
}
func
TestExprEval
(
t
*
testing
.
T
)
{
for
i
,
tt
:=
range
exprEvalTests
{
t
.
Run
(
fmt
.
Sprint
(
i
),
func
(
t
*
testing
.
T
)
{
x
,
err
:=
parseExpr
(
tt
.
in
)
if
err
!=
nil
{
t
.
Fatal
(
err
)
}
tags
:=
make
(
map
[
string
]
bool
)
wantTags
:=
make
(
map
[
string
]
bool
)
for
_
,
tag
:=
range
strings
.
Fields
(
tt
.
tags
)
{
wantTags
[
tag
]
=
true
}
hasTag
:=
func
(
tag
string
)
bool
{
tags
[
tag
]
=
true
return
tag
==
"yes"
}
ok
:=
x
.
Eval
(
hasTag
)
if
ok
!=
tt
.
ok
||
!
reflect
.
DeepEqual
(
tags
,
wantTags
)
{
t
.
Errorf
(
"Eval(%#q):
\n
have ok=%v, tags=%v
\n
want ok=%v, tags=%v"
,
tt
.
in
,
ok
,
tags
,
tt
.
ok
,
wantTags
)
}
})
}
}
var
parsePlusBuildExprTests
=
[]
struct
{
in
string
x
Expr
}{
{
"x"
,
tag
(
"x"
)},
{
"x,y"
,
and
(
tag
(
"x"
),
tag
(
"y"
))},
{
"x y"
,
or
(
tag
(
"x"
),
tag
(
"y"
))},
{
"x y,z"
,
or
(
tag
(
"x"
),
and
(
tag
(
"y"
),
tag
(
"z"
)))},
{
"x,y z"
,
or
(
and
(
tag
(
"x"
),
tag
(
"y"
)),
tag
(
"z"
))},
{
"x,!y !z"
,
or
(
and
(
tag
(
"x"
),
not
(
tag
(
"y"
))),
not
(
tag
(
"z"
)))},
{
"!! x"
,
or
(
tag
(
"ignore"
),
tag
(
"x"
))},
{
"!!x"
,
tag
(
"ignore"
)},
{
"!x"
,
not
(
tag
(
"x"
))},
{
"!"
,
tag
(
"ignore"
)},
{
""
,
tag
(
"ignore"
)},
}
var
constraintTests
=
[]
struct
{
in
string
x
Expr
err
string
}{
{
"#wa:build x && y"
,
and
(
tag
(
"x"
),
tag
(
"y"
)),
""
},
{
"#wa:build x && y
\n
"
,
and
(
tag
(
"x"
),
tag
(
"y"
)),
""
},
{
"#wa:build x && y
\n
"
,
nil
,
"not a build constraint"
},
{
"#wa:build x && y
\n
more"
,
nil
,
"not a build constraint"
},
{
" #wa:build x && y"
,
nil
,
"not a build constraint"
},
{
"#wa:build
\n
"
,
nil
,
"unexpected end of expression"
},
}
func
TestParse
(
t
*
testing
.
T
)
{
for
i
,
tt
:=
range
constraintTests
{
t
.
Run
(
fmt
.
Sprint
(
i
),
func
(
t
*
testing
.
T
)
{
x
,
err
:=
Parse
(
tt
.
in
)
if
err
!=
nil
{
if
tt
.
err
==
""
{
t
.
Errorf
(
"Constraint(%q): unexpected error: %v"
,
tt
.
in
,
err
)
}
else
if
!
strings
.
Contains
(
err
.
Error
(),
tt
.
err
)
{
t
.
Errorf
(
"Constraint(%q): error %v, want %v"
,
tt
.
in
,
err
,
tt
.
err
)
}
return
}
if
tt
.
err
!=
""
{
t
.
Errorf
(
"Constraint(%q) = %v, want error %v"
,
tt
.
in
,
x
,
tt
.
err
)
return
}
if
x
.
String
()
!=
tt
.
x
.
String
()
{
t
.
Errorf
(
"Constraint(%q):
\n
have %v
\n
want %v"
,
tt
.
in
,
x
,
tt
.
x
)
}
})
}
}
var
plusBuildLinesTests
=
[]
struct
{
in
string
out
[]
string
err
error
}{
{
"x"
,
[]
string
{
"x"
},
nil
},
{
"x && !y"
,
[]
string
{
"x,!y"
},
nil
},
{
"x || y"
,
[]
string
{
"x y"
},
nil
},
{
"x && (y || z)"
,
[]
string
{
"x"
,
"y z"
},
nil
},
{
"!(x && y)"
,
[]
string
{
"!x !y"
},
nil
},
{
"x || (y && z)"
,
[]
string
{
"x y,z"
},
nil
},
{
"w && (x || (y && z))"
,
[]
string
{
"w"
,
"x y,z"
},
nil
},
}
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录