Skip to content

Commit 58ba5e0

Browse files
authored
Update the RIDL error syntax & parser (webrpc#191)
* Remove noisy log in TestSyntaxError() * Update the RIDL error syntax & parser error <code> <name> <message> [HTTP <status code>]
1 parent 28be4f5 commit 58ba5e0

4 files changed

Lines changed: 114 additions & 36 deletions

File tree

schema/ridl/error_parser.go

Lines changed: 72 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -2,62 +2,103 @@ package ridl
22

33
import (
44
"fmt"
5+
6+
"github.com/pkg/errors"
57
)
68

7-
func parserStateErrorDefinition(et *ErrorNode) parserState {
9+
// error <code> <name> <message> [HTTP <status code>]
10+
func parserStateError(p *parser) parserState {
11+
// error <code> <name>
12+
matches, err := p.match(tokenWord, tokenWhitespace, tokenWord, tokenWhitespace, tokenWord, tokenWhitespace)
13+
if err != nil {
14+
return p.stateError(err)
15+
}
16+
17+
if matches[0].val != wordError {
18+
return p.stateError(errUnexpectedToken)
19+
}
20+
21+
if err := expectNumber(matches[2], matches[2].val); err != nil {
22+
return p.stateError(fmt.Errorf("expecting error code to be a number but got '%s'", matches[2].val))
23+
}
24+
25+
return parserStateErrorMessage(&ErrorNode{
26+
code: newTokenNode(matches[2]),
27+
name: newTokenNode(matches[4]),
28+
})
29+
}
30+
31+
// <message> [HTTP <status code>]
32+
func parserStateErrorMessage(et *ErrorNode) parserState {
833
return func(p *parser) parserState {
934
var err error
1035
tok := p.cursor()
1136

37+
// message, err := p.expectLiteralValue()
38+
// if err != nil {
39+
// return p.stateError(err)
40+
// }
41+
1242
switch tok.tt {
1343

44+
// "<message>"
1445
case tokenQuote:
1546
tok, err = p.expectStringValue()
1647
if err != nil {
1748
return p.stateError(err)
1849
}
1950
et.message = newTokenNode(tok)
2051

21-
case tokenMinusSign:
22-
matches, err := p.match(tokenDash, tokenDash, tokenWhitespace, tokenWord)
23-
if err != nil {
24-
return p.stateError(err)
25-
}
26-
et.httpStatus = newTokenNode(matches[3])
27-
28-
case tokenWhitespace, tokenNewLine:
52+
// <message>
53+
case tokenWord:
54+
et.message = newTokenNode(tok)
2955
p.next()
3056

31-
case tokenHash:
32-
p.continueUntilEOL()
33-
3457
default:
58+
return p.stateError(errors.Errorf("expected <message> but got %v", tok))
59+
}
60+
61+
return parserStateErrorExplicitStatusCode(et)
62+
}
63+
}
64+
65+
// [HTTP <status code>]
66+
func parserStateErrorExplicitStatusCode(et *ErrorNode) parserState {
67+
return func(p *parser) parserState {
68+
// Try to match HTTP
69+
matches, err := p.match(tokenWhitespace, tokenWord)
70+
if err != nil {
71+
if err := p.expectOptionalCommentOrEOL(); err != nil {
72+
return p.stateError(err)
73+
}
74+
3575
p.emit(et)
3676
return parserDefaultState
77+
}
3778

79+
if err := expectWord(matches[1], "HTTP"); err != nil {
80+
p.rewind(1)
81+
return p.stateError(fmt.Errorf("expecting optional 'HTTP <status code>' but got '%s'", matches[1].val))
3882
}
3983

40-
return parserStateErrorDefinition(et)
41-
}
42-
}
84+
// Match <status code>
85+
matches, err = p.match(tokenWhitespace, tokenWord)
86+
if err != nil {
87+
return p.stateError(fmt.Errorf("expecting '<status code>': %w", err))
88+
}
4389

44-
func parserStateError(p *parser) parserState {
45-
// error <code> <name> <message> [-- <http status code>]
46-
matches, err := p.match(tokenWord, tokenWhitespace, tokenWord, tokenWhitespace, tokenWord, tokenWhitespace)
47-
if err != nil {
48-
return p.stateError(err)
49-
}
90+
if err := expectNumber(matches[1], matches[1].val); err != nil {
91+
p.rewind(1)
92+
return p.stateError(fmt.Errorf("expecting HTTP '<status code>' to be a number but got '%s'", matches[1].val))
93+
}
5094

51-
if matches[0].val != wordError {
52-
return p.stateError(errUnexpectedToken)
53-
}
95+
et.httpStatus = newTokenNode(matches[1])
5496

55-
if err := expectNumber(matches[2], matches[2].val); err != nil {
56-
return p.stateError(fmt.Errorf("expecting error code to be a number but got '%s'", matches[2].val))
57-
}
97+
if err := p.expectOptionalCommentOrEOL(); err != nil {
98+
return p.stateError(err)
99+
}
58100

59-
return parserStateErrorDefinition(&ErrorNode{
60-
code: newTokenNode(matches[2]),
61-
name: newTokenNode(matches[4]),
62-
})
101+
p.emit(et)
102+
return parserDefaultState
103+
}
63104
}

schema/ridl/parser.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -391,7 +391,7 @@ func parserStateDeclaration(p *parser) parserState {
391391
// - <value> [<# comment>]
392392
return parserStateImport
393393
case wordError:
394-
// error <code> <name> <message>
394+
// error <code> <name> <message> [HTTP <status code>]
395395
return parserStateError
396396
case wordEnum:
397397
// enum <name>: <type>

schema/ridl/parser_test.go

Lines changed: 41 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -110,14 +110,52 @@ func TestParserTopLevelDefinitions(t *testing.T) {
110110

111111
func TestParserError(t *testing.T) {
112112
p, err := newStringParser(`
113-
error 12345 InvalidUsername "username is invalid" -- 401
114-
error 45678 Unauthorized "unauthorized access"
113+
error 12345 InvalidUsername "username is invalid" HTTP 401
114+
error 12345 InvalidUsername "username is invalid" HTTP 1 # comment
115+
error 12345 InvalidUsername InvalidUsername
116+
error 12345 InvalidUsername InvalidUsername # comment
117+
error 45678 Unauthorized "unauthorized access" HTTP 401
118+
error 45678 Unauthorized "unauthorized access" HTTP 401 # comment
119+
error 45678 Unauthorized Unauthorized HTTP 401
120+
error 45678 Unauthorized Unauthorized HTTP 401 # comment
115121
`)
116122
assert.NoError(t, err)
117123

118124
err = p.run()
119125
assert.NoError(t, err)
120-
assert.Equal(t, 2, len(p.root.Errors()))
126+
127+
if !assert.Equal(t, 8, len(p.root.Errors())) {
128+
for _, e := range p.root.Errors() {
129+
t.Logf("%v", e.message)
130+
}
131+
}
132+
}
133+
134+
func TestParserErrorInvalid(t *testing.T) {
135+
tt := []string{
136+
`error`,
137+
`error WRONG`,
138+
`error 12345`,
139+
`error 12345 Unauthorized`, // missing <message>
140+
`error 12345 Unauthorized unauthorized access`, // missing quotes for multi-word <message>
141+
`error 12345 Unauthorized "unauthorized access" WRONG 401`,
142+
`error 12345 Unauthorized "unauthorized access" HTTP STATUS`,
143+
`error 12345 Unauthorized "unauthorized access" HTTP 401 EXTRA`,
144+
}
145+
146+
for _, str := range tt {
147+
p, err := newStringParser(str)
148+
assert.NoError(t, err)
149+
150+
err = p.run()
151+
assert.Error(t, err)
152+
153+
if !assert.Equal(t, 0, len(p.root.Errors())) {
154+
for _, e := range p.root.Errors() {
155+
t.Logf("%v", e.message)
156+
}
157+
}
158+
}
121159
}
122160

123161
func TestParserImport(t *testing.T) {

schema/ridl/syntax_error_test.go

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,5 @@ func TestSyntaxError(t *testing.T) {
4444
for i := range syntaxErrors {
4545
_, err := parseString(syntaxErrors[i])
4646
assert.Error(t, err)
47-
t.Logf("%v", err)
4847
}
4948
}

0 commit comments

Comments
 (0)