Skip to content

Commit abc0b15

Browse files
authored
Merge pull request #37 from github/elr/paren-bug
improve checking for syntax errors
2 parents 9f1f925 + a83a0a1 commit abc0b15

File tree

2 files changed

+214
-4
lines changed

2 files changed

+214
-4
lines changed

spdxexp/parse.go

Lines changed: 90 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -29,14 +29,44 @@ func parse(source string) (*node, error) {
2929
}
3030

3131
func (t *tokenStream) parseTokens() *node {
32+
if len(t.tokens) == 0 {
33+
// malformed with no tokens
34+
t.err = errors.New("no tokens to parse")
35+
return nil
36+
}
37+
3238
node := t.parseExpression()
3339
if t.err != nil {
3440
return nil
3541
}
36-
if node == nil || t.hasMore() {
42+
43+
if node == nil {
44+
// unable to parse expression for unknown reason
45+
t.err = errors.New("syntax error")
46+
return nil
47+
} else if t.hasMore() {
48+
// malformed with too many tokens - try to determine the cause
49+
50+
// check for close parenthesis without matching open parenthesis
51+
closeParen := t.parseOperator(")")
52+
if closeParen != nil {
53+
t.err = errors.New("close parenthesis does not have a matching open parenthesis")
54+
return nil
55+
}
56+
57+
// check for licenses without operator
58+
lic := t.parseLicense()
59+
if lic != nil {
60+
t.err = errors.New("licenses or expressions are not separated by an operator")
61+
return nil
62+
}
63+
64+
// cannot determine what syntax error occurred
3765
t.err = errors.New("syntax error")
3866
return nil
3967
}
68+
69+
// all is well
4070
return node
4171
}
4272

@@ -75,9 +105,15 @@ func (t *tokenStream) parseParenthesizedExpression() *node {
75105
return nil
76106
}
77107

108+
if !t.hasMore() {
109+
// no more tokens, so missing closing paren
110+
t.err = errors.New("open parenthesis does not have a matching close parenthesis")
111+
return nil
112+
}
113+
78114
closeParen := t.parseOperator(")")
79115
if closeParen == nil {
80-
t.err = errors.New("expected ')'")
116+
t.err = errors.New("open parenthesis does not have a matching close parenthesis")
81117
return nil
82118
}
83119

@@ -109,6 +145,44 @@ func (t *tokenStream) parseAtom() *node {
109145
return licenseNode
110146
}
111147

148+
// no atom found - try to determine the cause
149+
if t.hasMore() {
150+
// check for operators
151+
operator := t.parseOperator(")")
152+
if operator != nil {
153+
if t.index == 1 {
154+
t.err = errors.New("expression starts with close parenthesis")
155+
} else {
156+
t.err = errors.New("expected license or expression, but found close parenthesis")
157+
}
158+
return nil
159+
}
160+
161+
operator = t.parseOperator("OR")
162+
if operator != nil {
163+
if t.index == 1 {
164+
t.err = errors.New("expression starts with OR")
165+
} else {
166+
t.err = errors.New("expected license or expression, but found OR")
167+
}
168+
return nil
169+
}
170+
171+
operator = t.parseOperator("AND")
172+
if operator != nil {
173+
if t.index == 1 {
174+
t.err = errors.New("expression starts with AND")
175+
} else {
176+
t.err = errors.New("expected license or expression, but found AND")
177+
}
178+
return nil
179+
}
180+
181+
// cannot determine what syntax error occurred
182+
t.err = errors.New("syntax error")
183+
return nil
184+
}
185+
112186
t.err = errors.New("expected node, but found none")
113187
return nil
114188
}
@@ -132,12 +206,18 @@ func (t *tokenStream) parseExpression() *node {
132206
}
133207
op := strings.ToLower(*operator)
134208

209+
if !t.hasMore() {
210+
// expression found and no more tokens to process
211+
t.err = errors.New("expected expression following OR, but found none")
212+
return nil
213+
}
214+
135215
right := t.parseExpression()
136216
if t.err != nil {
137217
return nil
138218
}
139219
if right == nil {
140-
t.err = errors.New("expected expression, but found none")
220+
t.err = errors.New("expected expression following OR, but found none")
141221
return nil
142222
}
143223

@@ -172,12 +252,18 @@ func (t *tokenStream) parseAnd() *node {
172252
return left
173253
}
174254

255+
if !t.hasMore() {
256+
// expression found and no more tokens to process
257+
t.err = errors.New("expected expression following AND, but found none")
258+
return nil
259+
}
260+
175261
right := t.parseAnd()
176262
if t.err != nil {
177263
return nil
178264
}
179265
if right == nil {
180-
t.err = errors.New("expected expression, but found none")
266+
t.err = errors.New("expected expression following AND, but found none")
181267
return nil
182268
}
183269

spdxexp/parse_test.go

Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1075,6 +1075,42 @@ func TestParseTokens(t *testing.T) {
10751075
},
10761076
"{ LEFT: { LEFT: MIT and RIGHT: Apache-1.0+ } or RIGHT: { LEFT: DocumentRef-spdx-tool-1.2:LicenseRef-MIT-Style-2 or RIGHT: GPL-2.0 with Bison-exception-2.2 } }", nil,
10771077
},
1078+
{"operator error - missing close parenthesis", getMissingEndParenTokens(0),
1079+
&node{}, "", errors.New("open parenthesis does not have a matching close parenthesis"),
1080+
},
1081+
{"operator error - missing open parenthesis", getMissingStartParenTokens(0),
1082+
&node{}, "", errors.New("close parenthesis does not have a matching open parenthesis"),
1083+
},
1084+
{"operator error - missing operator", getMissingOperatorTokens(0),
1085+
&node{}, "", errors.New("licenses or expressions are not separated by an operator"),
1086+
},
1087+
{"operator error - missing license after OR", getMissingSecondLicenseInORTokens(0),
1088+
&node{}, "", errors.New("expected expression following OR, but found none"),
1089+
},
1090+
{"operator error - missing license after AND", getMissingSecondLicenseInANDTokens(0),
1091+
&node{}, "", errors.New("expected expression following AND, but found none"),
1092+
},
1093+
{"operator error - starts with close parenthesis", getStartsWithCloseParenTokens(0),
1094+
&node{}, "", errors.New("expression starts with close parenthesis"),
1095+
},
1096+
{"operator error - starts with OR", getStartsWithORTokens(0),
1097+
&node{}, "", errors.New("expression starts with OR"),
1098+
},
1099+
{"operator error - starts with AND", getStartsWithANDTokens(0),
1100+
&node{}, "", errors.New("expression starts with AND"),
1101+
},
1102+
{"operator error - ends with OR", getEndsWithORTokens(0),
1103+
&node{}, "", errors.New("expected expression following OR, but found none"),
1104+
},
1105+
{"operator error - ends with AND", getEndsWithANDTokens(0),
1106+
&node{}, "", errors.New("expected expression following AND, but found none"),
1107+
},
1108+
{"operator error - OR immediately after operator", getDoubleORTokens(0),
1109+
&node{}, "", errors.New("expected license or expression, but found OR"),
1110+
},
1111+
{"operator error - AND immediately after operator", getDoubleANDTokens(0),
1112+
&node{}, "", errors.New("expected license or expression, but found AND"),
1113+
},
10781114
}
10791115

10801116
for _, test := range tests {
@@ -1328,6 +1364,94 @@ func getKitchSinkTokens(index int) *tokenStream {
13281364
return getTokenStream(tokens, index)
13291365
}
13301366

1367+
func getMissingOperatorTokens(index int) *tokenStream {
1368+
var tokens []token
1369+
tokens = append(tokens, token{role: licenseToken, value: "MIT"})
1370+
tokens = append(tokens, token{role: licenseToken, value: "Apache-2.0"})
1371+
return getTokenStream(tokens, index)
1372+
}
1373+
1374+
func getMissingSecondLicenseInORTokens(index int) *tokenStream {
1375+
var tokens []token
1376+
tokens = append(tokens, token{role: licenseToken, value: "MIT"})
1377+
tokens = append(tokens, token{role: operatorToken, value: "OR"})
1378+
return getTokenStream(tokens, index)
1379+
}
1380+
1381+
func getMissingSecondLicenseInANDTokens(index int) *tokenStream {
1382+
var tokens []token
1383+
tokens = append(tokens, token{role: licenseToken, value: "MIT"})
1384+
tokens = append(tokens, token{role: operatorToken, value: "AND"})
1385+
return getTokenStream(tokens, index)
1386+
}
1387+
1388+
func getStartsWithCloseParenTokens(index int) *tokenStream {
1389+
var tokens []token
1390+
tokens = append(tokens, token{role: operatorToken, value: ")"})
1391+
tokens = append(tokens, token{role: licenseToken, value: "MIT"})
1392+
return getTokenStream(tokens, index)
1393+
}
1394+
1395+
func getStartsWithORTokens(index int) *tokenStream {
1396+
var tokens []token
1397+
tokens = append(tokens, token{role: operatorToken, value: "OR"})
1398+
tokens = append(tokens, token{role: licenseToken, value: "MIT"})
1399+
return getTokenStream(tokens, index)
1400+
}
1401+
1402+
func getStartsWithANDTokens(index int) *tokenStream {
1403+
var tokens []token
1404+
tokens = append(tokens, token{role: operatorToken, value: "AND"})
1405+
tokens = append(tokens, token{role: licenseToken, value: "MIT"})
1406+
return getTokenStream(tokens, index)
1407+
}
1408+
1409+
func getEndsWithORTokens(index int) *tokenStream {
1410+
var tokens []token
1411+
tokens = append(tokens, token{role: licenseToken, value: "MIT"})
1412+
tokens = append(tokens, token{role: operatorToken, value: "OR"})
1413+
return getTokenStream(tokens, index)
1414+
}
1415+
1416+
func getEndsWithANDTokens(index int) *tokenStream {
1417+
var tokens []token
1418+
tokens = append(tokens, token{role: licenseToken, value: "MIT"})
1419+
tokens = append(tokens, token{role: operatorToken, value: "AND"})
1420+
return getTokenStream(tokens, index)
1421+
}
1422+
1423+
func getDoubleORTokens(index int) *tokenStream {
1424+
var tokens []token
1425+
tokens = append(tokens, token{role: licenseToken, value: "MIT"})
1426+
tokens = append(tokens, token{role: operatorToken, value: "OR"})
1427+
tokens = append(tokens, token{role: operatorToken, value: "OR"})
1428+
tokens = append(tokens, token{role: licenseToken, value: "Apache-2.0"})
1429+
return getTokenStream(tokens, index)
1430+
}
1431+
1432+
func getDoubleANDTokens(index int) *tokenStream {
1433+
var tokens []token
1434+
tokens = append(tokens, token{role: licenseToken, value: "MIT"})
1435+
tokens = append(tokens, token{role: operatorToken, value: "AND"})
1436+
tokens = append(tokens, token{role: operatorToken, value: "AND"})
1437+
tokens = append(tokens, token{role: licenseToken, value: "Apache-2.0"})
1438+
return getTokenStream(tokens, index)
1439+
}
1440+
1441+
func getMissingStartParenTokens(index int) *tokenStream {
1442+
var tokens []token
1443+
tokens = append(tokens, token{role: licenseToken, value: "MIT"})
1444+
tokens = append(tokens, token{role: operatorToken, value: ")"})
1445+
return getTokenStream(tokens, index)
1446+
}
1447+
1448+
func getMissingEndParenTokens(index int) *tokenStream {
1449+
var tokens []token
1450+
tokens = append(tokens, token{role: operatorToken, value: "("})
1451+
tokens = append(tokens, token{role: licenseToken, value: "MIT"})
1452+
return getTokenStream(tokens, index)
1453+
}
1454+
13311455
func getTokenStream(tokens []token, index int) *tokenStream {
13321456
return &tokenStream{
13331457
tokens: tokens,

0 commit comments

Comments
 (0)