Skip to content

Commit

Permalink
style: change parsing errors
Browse files Browse the repository at this point in the history
  • Loading branch information
favonia committed Mar 13, 2023
1 parent 479ecb7 commit 90fb392
Show file tree
Hide file tree
Showing 9 changed files with 111 additions and 103 deletions.
2 changes: 1 addition & 1 deletion internal/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -347,7 +347,7 @@ func (c *Config) NormalizeConfig(ppfmt pp.PP) bool {
}

// fill in proxyMap
proxiedPred, ok := domainexp.ParseExpression(ppfmt, c.ProxiedTemplate)
proxiedPred, ok := domainexp.ParseExpression(ppfmt, "PROXIED", c.ProxiedTemplate)
if !ok {
return false
}
Expand Down
7 changes: 4 additions & 3 deletions internal/config/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -530,6 +530,7 @@ func TestReadEnvEmpty(t *testing.T) {
func TestNormalizeConfig(t *testing.T) {
t.Parallel()

keyProxied := "PROXIED"
var empty config.Config

for name, tc := range map[string]struct {
Expand Down Expand Up @@ -719,7 +720,7 @@ func TestNormalizeConfig(t *testing.T) {
m.EXPECT().IsEnabledFor(pp.Info).Return(true),
m.EXPECT().Infof(pp.EmojiEnvVars, "Checking settings . . ."),
m.EXPECT().IncIndent().Return(m),
m.EXPECT().Errorf(pp.EmojiUserError, "Failed to parse %q: wanted a boolean expression; got %q", `range`, `range`),
m.EXPECT().Errorf(pp.EmojiUserError, "%s (%q) is not a boolean expression: got unexpected token %q", keyProxied, `range`, `range`), //nolint:lll
)
},
},
Expand All @@ -740,7 +741,7 @@ func TestNormalizeConfig(t *testing.T) {
m.EXPECT().IsEnabledFor(pp.Info).Return(true),
m.EXPECT().Infof(pp.EmojiEnvVars, "Checking settings . . ."),
m.EXPECT().IncIndent().Return(m),
m.EXPECT().Errorf(pp.EmojiUserError, "Failed to parse %q: wanted a boolean expression; got %q", "999", "999"),
m.EXPECT().Errorf(pp.EmojiUserError, "%s (%q) is not a boolean expression: got unexpected token %q", keyProxied, `999`, `999`), //nolint:lll
)
},
},
Expand All @@ -761,7 +762,7 @@ func TestNormalizeConfig(t *testing.T) {
m.EXPECT().IsEnabledFor(pp.Info).Return(true),
m.EXPECT().Infof(pp.EmojiEnvVars, "Checking settings . . ."),
m.EXPECT().IncIndent().Return(m),
m.EXPECT().Errorf(pp.EmojiUserError, `Failed to parse %q: wanted %q; reached end of string`, `is(12345`, ")"),
m.EXPECT().Errorf(pp.EmojiUserError, `%s (%q) is missing %q at the end`, keyProxied, `is(12345`, ")"),
)
},
},
Expand Down
2 changes: 1 addition & 1 deletion internal/config/env.go
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,7 @@ func ReadTTL(ppfmt pp.PP, key string, field *api.TTL) bool {

// ReadDomains reads an environment variable as a comma-separated list of domains.
func ReadDomains(ppfmt pp.PP, key string, field *[]domain.Domain) bool {
if list, ok := domainexp.ParseList(ppfmt, Getenv(key)); ok {
if list, ok := domainexp.ParseList(ppfmt, key, Getenv(key)); ok {
*field = list
return true
}
Expand Down
22 changes: 11 additions & 11 deletions internal/config/env_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -418,21 +418,21 @@ func TestReadDomains(t *testing.T) {
"test1": {true, "書.org , Bücher.org ", ds{f("random.org")}, ds{f("xn--rov.org"), f("xn--bcher-kva.org")}, true, nil}, //nolint:lll
"test2": {true, " \txn--rov.org , xn--Bcher-kva.org ", ds{f("random.org")}, ds{f("xn--rov.org"), f("xn--bcher-kva.org")}, true, nil}, //nolint:lll
"illformed1": {
true, "xn--:D.org",
true, "xn--:D.org,a.org",
ds{f("random.org")},
ds{f("xn--:d.org")},
true,
ds{f("random.org")},
false,
func(m *mocks.MockPP) {
m.EXPECT().Warningf(pp.EmojiUserError, "Domain %q was added but it is ill-formed: %v", "xn--:d.org", gomock.Any()) //nolint:lll
m.EXPECT().Errorf(pp.EmojiUserError, "%s (%q) contains an ill-formed domain %q: %v", key, "xn--:D.org,a.org", "xn--:d.org", gomock.Any()) //nolint:lll
},
},
"illformed2": {
true, "*.xn--:D.org",
true, "*.xn--:D.org,a.org",
ds{f("random.org")},
ds{w("xn--:d.org")},
true,
ds{f("random.org")},
false,
func(m *mocks.MockPP) {
m.EXPECT().Warningf(pp.EmojiUserError, "Domain %q was added but it is ill-formed: %v", "*.xn--:d.org", gomock.Any()) //nolint:lll
m.EXPECT().Errorf(pp.EmojiUserError, "%s (%q) contains an ill-formed domain %q: %v", key, "*.xn--:D.org,a.org", "*.xn--:d.org", gomock.Any()) //nolint:lll
},
},
"illformed3": {
Expand All @@ -441,7 +441,7 @@ func TestReadDomains(t *testing.T) {
ds{},
false,
func(m *mocks.MockPP) {
m.EXPECT().Errorf(pp.EmojiUserError, "Failed to parse %q: unexpected token %q", "hi.org,(", "(")
m.EXPECT().Errorf(pp.EmojiUserError, "%s (%q) has unexpected token %q", key, "hi.org,(", "(")
},
},
"illformed4": {
Expand All @@ -450,7 +450,7 @@ func TestReadDomains(t *testing.T) {
ds{},
false,
func(m *mocks.MockPP) {
m.EXPECT().Errorf(pp.EmojiUserError, "Failed to parse %q: unexpected token %q", ")", ")")
m.EXPECT().Errorf(pp.EmojiUserError, "%s (%q) has unexpected token %q", key, ")", ")")
},
},
} {
Expand Down Expand Up @@ -511,7 +511,7 @@ func TestReadProvider(t *testing.T) {
func(m *mocks.MockPP) {
m.EXPECT().Warningf(
pp.EmojiUserWarning,
`%s=cloudflare is deprecated; use %s=cloudflare.trace or %s=cloudflare.doh`, //nolint:lll
`%s=cloudflare is deprecated; use %s=cloudflare.trace or %s=cloudflare.doh`,
keyDeprecated, key, key,
)
},
Expand Down
4 changes: 2 additions & 2 deletions internal/domainexp/lexer.go
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ func splitter(data []byte, atEOF bool) (int, []byte, error) {
}
}

func tokenize(ppfmt pp.PP, input string) ([]string, bool) {
func tokenize(ppfmt pp.PP, key string, input string) ([]string, bool) {
scanner := bufio.NewScanner(strings.NewReader(input))
scanner.Split(splitter)

Expand All @@ -110,7 +110,7 @@ func tokenize(ppfmt pp.PP, input string) ([]string, bool) {
}

if err := scanner.Err(); err != nil {
ppfmt.Errorf(pp.EmojiUserError, "Failed to parse %q: %v", input, err)
ppfmt.Errorf(pp.EmojiUserError, "%s (%q) is ill-formed: %v", key, input, err)
return nil, false
}
return tokens, true
Expand Down
95 changes: 48 additions & 47 deletions internal/domainexp/parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import (
"github.com/favonia/cloudflare-ddns/internal/pp"
)

func scanList(ppfmt pp.PP, input string, tokens []string) ([]string, []string) {
func scanList(ppfmt pp.PP, key string, input string, tokens []string) ([]string, []string) {
var list []string
readyForNext := true
for len(tokens) > 0 {
Expand All @@ -18,11 +18,11 @@ func scanList(ppfmt pp.PP, input string, tokens []string) ([]string, []string) {
case ")":
return list, tokens
case "(", "&&", "||", "!":
ppfmt.Errorf(pp.EmojiUserError, `Failed to parse %q: unexpected token %q`, input, tokens[0])
ppfmt.Errorf(pp.EmojiUserError, `%s (%q) has unexpected token %q`, key, input, tokens[0])
return nil, nil
default:
if !readyForNext {
ppfmt.Warningf(pp.EmojiUserError, `Please insert a comma "," before %q`, tokens[0])
ppfmt.Warningf(pp.EmojiUserError, `%s (%q) is missing a comma "," before %q`, key, input, tokens[0])
}
list = append(list, tokens[0])
readyForNext = false
Expand All @@ -33,51 +33,52 @@ func scanList(ppfmt pp.PP, input string, tokens []string) ([]string, []string) {
return list, tokens
}

func scanASCIIDomainList(ppfmt pp.PP, input string, tokens []string) ([]string, []string) {
list, tokens := scanList(ppfmt, input, tokens)
func scanASCIIDomainList(ppfmt pp.PP, key string, input string, tokens []string) ([]string, []string) {
list, tokens := scanList(ppfmt, key, input, tokens)
domains := make([]string, 0, len(list))
for _, raw := range list {
domains = append(domains, domain.StringToASCII(raw))
}
return domains, tokens
}

func scanDomainList(ppfmt pp.PP, input string, tokens []string) ([]domain.Domain, []string) {
list, tokens := scanList(ppfmt, input, tokens)
func scanDomainList(ppfmt pp.PP, key string, input string, tokens []string) ([]domain.Domain, []string) {
list, tokens := scanList(ppfmt, key, input, tokens)
domains := make([]domain.Domain, 0, len(list))
for _, raw := range list {
domain, err := domain.New(raw)
if err != nil {
ppfmt.Warningf(pp.EmojiUserError,
"Domain %q was added but it is ill-formed: %v",
domain.Describe(), err)
ppfmt.Errorf(pp.EmojiUserError,
"%s (%q) contains an ill-formed domain %q: %v",
key, input, domain.Describe(), err)
return nil, nil
}
domains = append(domains, domain)
}
return domains, tokens
}

func scanConstants(_ppfmt pp.PP, _input string, tokens []string, wanted []string) (string, []string) {
func scanConstants(_ppfmt pp.PP, _key string, _input string, tokens []string, expected []string) (string, []string) {
if len(tokens) == 0 {
return "", nil
}
for _, wanted := range wanted {
if wanted == tokens[0] {
for _, expected := range expected {
if expected == tokens[0] {
return tokens[0], tokens[1:]
}
}
return "", nil
}

func scanMustConstant(ppfmt pp.PP, input string, tokens []string, wanted string) []string {
func scanMustConstant(ppfmt pp.PP, key string, input string, tokens []string, expected string) []string {
if len(tokens) == 0 {
ppfmt.Errorf(pp.EmojiUserError, `Failed to parse %q: wanted %q; reached end of string`, input, wanted)
ppfmt.Errorf(pp.EmojiUserError, `%s (%q) is missing %q at the end`, key, input, expected)
return nil
}
if wanted == tokens[0] {
if expected == tokens[0] {
return tokens[1:]
}
ppfmt.Errorf(pp.EmojiUserError, `Failed to parse %q: wanted %q; got %q`, input, wanted, tokens[0])
ppfmt.Errorf(pp.EmojiUserError, `%s (%q) has unexpected token %q when %q is expected`, key, input, tokens[0], expected)
return nil
}

Expand All @@ -92,31 +93,31 @@ func hasStrictSuffix(s, suffix string) bool {
// <factor> --> true | false | <fun> | ! <factor> | ( <expression> )
//
//nolint:funlen
func scanFactor(ppfmt pp.PP, input string, tokens []string) (predicate, []string) {
func scanFactor(ppfmt pp.PP, key string, input string, tokens []string) (predicate, []string) {
// fmt.Printf("scanFactor(tokens = %#v)\n", tokens)

if _, newTokens := scanConstants(ppfmt, input, tokens,
if _, newTokens := scanConstants(ppfmt, key, input, tokens,
[]string{"1", "t", "T", "TRUE", "true", "True"}); newTokens != nil {
return func(_ domain.Domain) bool { return true }, newTokens
}

if _, newTokens := scanConstants(ppfmt, input, tokens,
if _, newTokens := scanConstants(ppfmt, key, input, tokens,
[]string{"0", "f", "F", "FALSE", "false", "False"}); newTokens != nil {
return func(_ domain.Domain) bool { return false }, newTokens
}

{
//nolint:nestif
if funName, newTokens := scanConstants(ppfmt, input, tokens, []string{"is", "sub"}); newTokens != nil {
newTokens = scanMustConstant(ppfmt, input, newTokens, "(")
if funName, newTokens := scanConstants(ppfmt, key, input, tokens, []string{"is", "sub"}); newTokens != nil {
newTokens = scanMustConstant(ppfmt, key, input, newTokens, "(")
if newTokens == nil {
return nil, nil
}
ASCIIDomains, newTokens := scanASCIIDomainList(ppfmt, input, newTokens)
ASCIIDomains, newTokens := scanASCIIDomainList(ppfmt, key, input, newTokens)
if newTokens == nil {
return nil, nil
}
newTokens = scanMustConstant(ppfmt, input, newTokens, ")")
newTokens = scanMustConstant(ppfmt, key, input, newTokens, ")")
if newTokens == nil {
return nil, nil
}
Expand Down Expand Up @@ -145,23 +146,23 @@ func scanFactor(ppfmt pp.PP, input string, tokens []string) (predicate, []string
}

{
_, newTokens := scanConstants(ppfmt, input, tokens, []string{"!"})
_, newTokens := scanConstants(ppfmt, key, input, tokens, []string{"!"})
if newTokens != nil {
if pred, newTokens := scanFactor(ppfmt, input, newTokens); newTokens != nil {
if pred, newTokens := scanFactor(ppfmt, key, input, newTokens); newTokens != nil {
return func(d domain.Domain) bool { return !(pred(d)) }, newTokens
}
return nil, nil
}
}

{
_, newTokens := scanConstants(ppfmt, input, tokens, []string{"("})
_, newTokens := scanConstants(ppfmt, key, input, tokens, []string{"("})
if newTokens != nil {
pred, newTokens := scanExpression(ppfmt, input, newTokens)
pred, newTokens := scanExpression(ppfmt, key, input, newTokens)
if newTokens == nil {
return nil, nil
}
newTokens = scanMustConstant(ppfmt, input, newTokens, ")")
newTokens = scanMustConstant(ppfmt, key, input, newTokens, ")")
if newTokens == nil {
return nil, nil
}
Expand All @@ -170,30 +171,30 @@ func scanFactor(ppfmt pp.PP, input string, tokens []string) (predicate, []string
}

if len(tokens) == 0 {
ppfmt.Errorf(pp.EmojiUserError, "Failed to parse %q: wanted a boolean expression; reached end of string", input)
ppfmt.Errorf(pp.EmojiUserError, "%s (%q) is not a boolean expression", key, input)
} else {
ppfmt.Errorf(pp.EmojiUserError, "Failed to parse %q: wanted a boolean expression; got %q", input, tokens[0])
ppfmt.Errorf(pp.EmojiUserError, "%s (%q) is not a boolean expression: got unexpected token %q", key, input, tokens[0])
}
return nil, nil
}

// scanTerm scans a term with this grammar:
//
// <term> --> <factor> "&&" <term> | <factor>
func scanTerm(ppfmt pp.PP, input string, tokens []string) (predicate, []string) {
func scanTerm(ppfmt pp.PP, key string, input string, tokens []string) (predicate, []string) {
// fmt.Printf("scanTerm(tokens = %#v)\n", tokens)

pred1, tokens := scanFactor(ppfmt, input, tokens)
pred1, tokens := scanFactor(ppfmt, key, input, tokens)
if tokens == nil {
return nil, nil
}

_, newTokens := scanConstants(ppfmt, input, tokens, []string{"&&"})
_, newTokens := scanConstants(ppfmt, key, input, tokens, []string{"&&"})
if newTokens == nil {
return pred1, tokens
}

pred2, newTokens := scanTerm(ppfmt, input, newTokens)
pred2, newTokens := scanTerm(ppfmt, key, input, newTokens)
if newTokens != nil {
return func(d domain.Domain) bool { return pred1(d) && pred2(d) }, newTokens
}
Expand All @@ -204,18 +205,18 @@ func scanTerm(ppfmt pp.PP, input string, tokens []string) (predicate, []string)
// scanExpression scans an expression with this grammar:
//
// <expression> --> <term> "||" <expression> | <term>
func scanExpression(ppfmt pp.PP, input string, tokens []string) (predicate, []string) {
pred1, tokens := scanTerm(ppfmt, input, tokens)
func scanExpression(ppfmt pp.PP, key string, input string, tokens []string) (predicate, []string) {
pred1, tokens := scanTerm(ppfmt, key, input, tokens)
if tokens == nil {
return nil, nil
}

_, newTokens := scanConstants(ppfmt, input, tokens, []string{"||"})
_, newTokens := scanConstants(ppfmt, key, input, tokens, []string{"||"})
if newTokens == nil {
return pred1, tokens
}

pred2, newTokens := scanExpression(ppfmt, input, newTokens)
pred2, newTokens := scanExpression(ppfmt, key, input, newTokens)
if newTokens != nil {
return func(d domain.Domain) bool { return pred1(d) || pred2(d) }, newTokens
}
Expand All @@ -224,17 +225,17 @@ func scanExpression(ppfmt pp.PP, input string, tokens []string) (predicate, []st
}

// ParseList parses a list of comma-separated domains. Internationalized domain names are fully supported.
func ParseList(ppfmt pp.PP, input string) ([]domain.Domain, bool) {
tokens, ok := tokenize(ppfmt, input)
func ParseList(ppfmt pp.PP, key string, input string) ([]domain.Domain, bool) {
tokens, ok := tokenize(ppfmt, key, input)
if !ok {
return nil, false
}

list, tokens := scanDomainList(ppfmt, input, tokens)
list, tokens := scanDomainList(ppfmt, key, input, tokens)
if tokens == nil {
return nil, false
} else if len(tokens) > 0 {
ppfmt.Errorf(pp.EmojiUserError, `Failed to parse %q: unexpected token %q`, input, tokens[0])
ppfmt.Errorf(pp.EmojiUserError, `%s (%q) has unexpected token %q`, key, input, tokens[0])
return nil, false
}

Expand All @@ -255,17 +256,17 @@ func ParseList(ppfmt pp.PP, input string) ([]domain.Domain, bool) {
// - exp1 && exp2, where exp1 and exp2 are boolean expressions, representing logical conjunction of exp1 and exp2.
//
// One can use parentheses to group expressions, such as !(is(hello.org) && (is(hello.io) || is(hello.me))).
func ParseExpression(ppfmt pp.PP, input string) (predicate, bool) {
tokens, ok := tokenize(ppfmt, input)
func ParseExpression(ppfmt pp.PP, key string, input string) (predicate, bool) {
tokens, ok := tokenize(ppfmt, key, input)
if !ok {
return nil, false
}

pred, tokens := scanExpression(ppfmt, input, tokens)
pred, tokens := scanExpression(ppfmt, key, input, tokens)
if tokens == nil {
return nil, false
} else if len(tokens) > 0 {
ppfmt.Errorf(pp.EmojiUserError, "Failed to parse %q: unexpected token %q", input, tokens[0])
ppfmt.Errorf(pp.EmojiUserError, "%s (%q) has unexpected token %q", key, input, tokens[0])
return nil, false
}

Expand Down
Loading

0 comments on commit 90fb392

Please sign in to comment.