-
Notifications
You must be signed in to change notification settings - Fork 2.2k
/
expression.go
81 lines (69 loc) · 1.87 KB
/
expression.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
package expression
import (
"strings"
"unicode"
"golang.org/x/xerrors"
)
var (
ErrInvalidExpression = xerrors.New("invalid expression error")
)
type NormalizeFunc func(license string) string
func parse(license string) (Expression, error) {
l := NewLexer(strings.NewReader(license))
if yyParse(l) != 0 {
return nil, xerrors.Errorf("license parse error: %w", l.Err())
} else if err := l.Err(); err != nil {
return nil, err
}
return l.result, nil
}
func Normalize(license string, fn ...NormalizeFunc) (string, error) {
expr, err := parse(license)
if err != nil {
return "", xerrors.Errorf("license (%s) parse error: %w", license, err)
}
expr = normalize(expr, fn...)
return expr.String(), nil
}
func normalize(expr Expression, fn ...NormalizeFunc) Expression {
switch e := expr.(type) {
case SimpleExpr:
for _, f := range fn {
e.license = f(e.license)
}
return e
case CompoundExpr:
e.left = normalize(e.left, fn...)
e.right = normalize(e.right, fn...)
e.conjunction.literal = strings.ToUpper(e.conjunction.literal) // e.g. "and" => "AND"
return e
}
return expr
}
// NormalizeForSPDX replaces ' ' to '-' in license-id.
// SPDX license MUST NOT be white space between a license-id.
// There MUST be white space on either side of the operator "WITH".
// ref: https://spdx.github.io/spdx-spec/v2.3/SPDX-license-expressions
func NormalizeForSPDX(s string) string {
var b strings.Builder
for _, c := range s {
switch {
// spec: idstring = 1*(ALPHA / DIGIT / "-" / "." )
case isAlphabet(c) || unicode.IsNumber(c) || c == '-' || c == '.':
_, _ = b.WriteRune(c)
case c == ':':
// TODO: Support DocumentRef
_, _ = b.WriteRune(c)
default:
// Replace invalid characters with '-'
_, _ = b.WriteRune('-')
}
}
return b.String()
}
func isAlphabet(r rune) bool {
if (r < 'a' || r > 'z') && (r < 'A' || r > 'Z') {
return false
}
return true
}