mirror of
https://github.com/MontFerret/ferret.git
synced 2025-08-13 19:52:52 +02:00
Refactor error handling for LIMIT clause: enhance diagnostics for dangling commas, improve error messages and hints, and introduce common error matching logic for better clarity and maintainability.
This commit is contained in:
@@ -10,6 +10,7 @@ func AnalyzeSyntaxError(src *file.Source, err *CompilationError, offending *Toke
|
||||
matchers := []SyntaxErrorMatcher{
|
||||
matchMissingAssignmentValue,
|
||||
matchForLoopErrors,
|
||||
matchCommonErrors,
|
||||
matchMissingReturnValue,
|
||||
}
|
||||
|
||||
|
@@ -114,33 +114,24 @@ func has(msg string, substr string) bool {
|
||||
return strings.Contains(strings.ToLower(msg), strings.ToLower(substr))
|
||||
}
|
||||
|
||||
func isNoAlternative(msg string) bool {
|
||||
return has(msg, "no viable alternative at input")
|
||||
}
|
||||
|
||||
func extractNoAlternativeInput(msg string) string {
|
||||
re := regexp.MustCompile(`no viable alternative at input\s+(?P<input>.+)`)
|
||||
match := re.FindStringSubmatch(msg)
|
||||
|
||||
return strings.Trim(match[re.SubexpIndex("input")], "'")
|
||||
}
|
||||
|
||||
func isExtraneous(msg string) bool {
|
||||
return has(msg, "extraneous input")
|
||||
}
|
||||
|
||||
func parseExtraneousInput(msg string) string {
|
||||
func extractExtraneousInput(msg string) string {
|
||||
re := regexp.MustCompile(`extraneous input\s+(?P<input>.+?)\s+expecting`)
|
||||
match := re.FindStringSubmatch(msg)
|
||||
return match[re.SubexpIndex("input")]
|
||||
}
|
||||
|
||||
func parseExtraneousInputAll(msg string) (string, []string) {
|
||||
rx := regexp.MustCompile(`extraneous input\s+(?P<input>.+?)\s+expecting\s+\{(?P<expected>.+?)\}`)
|
||||
matches := rx.FindStringSubmatch(msg)
|
||||
|
||||
if len(matches) != 3 {
|
||||
return "", nil
|
||||
}
|
||||
|
||||
input := strings.TrimSpace(matches[1])
|
||||
expectedRaw := strings.TrimSpace(matches[2])
|
||||
var expected []string
|
||||
|
||||
for _, part := range strings.Split(expectedRaw, ",") {
|
||||
part = strings.TrimSpace(part)
|
||||
part = strings.Trim(part, "'")
|
||||
expected = append(expected, part)
|
||||
}
|
||||
|
||||
return input, expected
|
||||
|
||||
return strings.Trim(match[re.SubexpIndex("input")], "'")
|
||||
}
|
||||
|
23
pkg/compiler/internal/diagnostics/match_common_errors.go
Normal file
23
pkg/compiler/internal/diagnostics/match_common_errors.go
Normal file
@@ -0,0 +1,23 @@
|
||||
package diagnostics
|
||||
|
||||
import "github.com/MontFerret/ferret/pkg/file"
|
||||
|
||||
func matchCommonErrors(src *file.Source, err *CompilationError, offending *TokenNode) bool {
|
||||
if isNoAlternative(err.Message) {
|
||||
if is(offending.Prev(), ",") {
|
||||
span := spanFromTokenSafe(offending.Prev().Token(), src)
|
||||
span.Start++
|
||||
span.End++
|
||||
|
||||
err.Message = "Expected expression after ','"
|
||||
err.Hint = "Did you forget to provide a value?"
|
||||
err.Spans = []ErrorSpan{
|
||||
NewMainErrorSpan(span, "missing value"),
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
@@ -1,6 +1,8 @@
|
||||
package diagnostics
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/MontFerret/ferret/pkg/file"
|
||||
)
|
||||
|
||||
@@ -93,9 +95,9 @@ func matchForLoopErrors(src *file.Source, err *CompilationError, offending *Toke
|
||||
}
|
||||
|
||||
if isExtraneous(err.Message) {
|
||||
input := parseExtraneousInput(err.Message)
|
||||
input := extractExtraneousInput(err.Message)
|
||||
|
||||
if input != "','" {
|
||||
if input != "," {
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -122,5 +124,47 @@ func matchForLoopErrors(src *file.Source, err *CompilationError, offending *Toke
|
||||
}
|
||||
}
|
||||
|
||||
if isNoAlternative(err.Message) {
|
||||
if is(prev, ",") {
|
||||
var steps int
|
||||
|
||||
// We walk back two tokens to find if the keyword is LIMIT.
|
||||
for ; steps < 2 && !is(prev, "LIMIT"); steps++ {
|
||||
prev = prev.Prev()
|
||||
}
|
||||
|
||||
if is(prev, "LIMIT") {
|
||||
span := spanFromTokenSafe(offending.Prev().Token(), src)
|
||||
span.Start++
|
||||
span.End++
|
||||
|
||||
err.Message = "Dangling comma in LIMIT clause"
|
||||
err.Hint = "LIMIT accepts one or two arguments. Did you forget to add a value?"
|
||||
err.Spans = []ErrorSpan{
|
||||
NewMainErrorSpan(span, "missing value"),
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
} else if is(offending, "LIMIT") {
|
||||
input := extractNoAlternativeInput(err.Message)
|
||||
tokens := strings.Fields(input)
|
||||
|
||||
if len(tokens) > 0 && has(tokens[len(tokens)-1], ",") {
|
||||
span := spanFromTokenSafe(offending.Token(), src)
|
||||
span.Start = span.End
|
||||
span.End = span.Start + 1
|
||||
|
||||
err.Message = "Dangling comma in LIMIT clause"
|
||||
err.Hint = "LIMIT accepts one or two arguments. Did you forget to add a value?"
|
||||
err.Spans = []ErrorSpan{
|
||||
NewMainErrorSpan(span, "missing value"),
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
@@ -157,8 +157,30 @@ func TestSyntaxErrors(t *testing.T) {
|
||||
RETURN x
|
||||
`, E{
|
||||
Kind: compiler.SyntaxError,
|
||||
Message: "---",
|
||||
Hint: "FILTER requires a boolean expression.",
|
||||
Message: "Dangling comma in LIMIT clause",
|
||||
Hint: "LIMIT accepts one or two arguments. Did you forget to add a value?",
|
||||
}, "LIMIT unexpected comma 2"),
|
||||
ErrorCase(
|
||||
`
|
||||
LET users = []
|
||||
FOR x IN users
|
||||
LIMIT ,
|
||||
RETURN x
|
||||
`, E{
|
||||
Kind: compiler.SyntaxError,
|
||||
Message: "Dangling comma in LIMIT clause",
|
||||
Hint: "LIMIT accepts one or two arguments. Did you forget to add a value?",
|
||||
}, "LIMIT unexpected comma 3"),
|
||||
ErrorCase(
|
||||
`
|
||||
LET users = []
|
||||
FOR x IN users
|
||||
LIMIT,
|
||||
RETURN x
|
||||
`, E{
|
||||
Kind: compiler.SyntaxError,
|
||||
Message: "Dangling comma in LIMIT clause",
|
||||
Hint: "LIMIT accepts one or two arguments. Did you forget to add a value?",
|
||||
}, "LIMIT unexpected comma 4"),
|
||||
})
|
||||
}
|
||||
|
Reference in New Issue
Block a user