mirror of
https://github.com/MontFerret/ferret.git
synced 2025-08-13 19:52:52 +02:00
Add comprehensive tests for unclosed string literals: enhance diagnostics and error hints for various cases of unclosed strings, including single, double, and backtick quotes, to improve clarity and coverage.
This commit is contained in:
@@ -9,6 +9,7 @@ type SyntaxErrorMatcher func(src *file.Source, err *CompilationError, offending
|
||||
func AnalyzeSyntaxError(src *file.Source, err *CompilationError, offending *TokenNode) bool {
|
||||
matchers := []SyntaxErrorMatcher{
|
||||
matchCommonErrors,
|
||||
matchLiteralErrors,
|
||||
matchMissingAssignmentValue,
|
||||
matchForLoopErrors,
|
||||
matchMissingReturnValue,
|
||||
|
144
pkg/compiler/internal/diagnostics/match_error_common.go
Normal file
144
pkg/compiler/internal/diagnostics/match_error_common.go
Normal file
@@ -0,0 +1,144 @@
|
||||
package diagnostics
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"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
|
||||
}
|
||||
|
||||
if is(offending.Prev(), "||") || is(offending.Prev(), "OR") ||
|
||||
is(offending.Prev(), "&&") || is(offending.Prev(), "AND") {
|
||||
span := spanFromTokenSafe(offending.Prev().Token(), src)
|
||||
span.Start += 2
|
||||
span.End += 2
|
||||
|
||||
operator := offending.Prev().GetText()
|
||||
err.Message = fmt.Sprintf("Expected right-hand expression after '%s'", operator)
|
||||
err.Hint = fmt.Sprintf("Provide an expression after the logical operator, e.g. (a %s b).", operator)
|
||||
err.Spans = []ErrorSpan{
|
||||
NewMainErrorSpan(span, "missing expression"),
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// Ternary operator, incomplete expression
|
||||
if is(offending.Prev(), "?") {
|
||||
span := spanFromTokenSafe(offending.Prev().Token(), src)
|
||||
span.Start++
|
||||
span.End++
|
||||
|
||||
err.Message = "Expected expression after '?' in ternary operator"
|
||||
err.Hint = "Provide an expression after the question mark to complete the ternary operation."
|
||||
err.Spans = []ErrorSpan{
|
||||
NewMainErrorSpan(span, "missing expression"),
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// Ternary operator, missing the right-hand expression
|
||||
if is(offending.Prev(), ":") {
|
||||
span := spanFromTokenSafe(offending.Prev().Token(), src)
|
||||
span.Start++
|
||||
span.End++
|
||||
|
||||
err.Message = "Expected expression after ':' in ternary operator"
|
||||
err.Hint = "Provide an expression after the colon to complete the ternary operation."
|
||||
err.Spans = []ErrorSpan{
|
||||
NewMainErrorSpan(span, "missing expression"),
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
if isNoAlternative(err.Message) || isMissing(err.Message) {
|
||||
if is(offending.Prev(), "(") {
|
||||
var span file.Span
|
||||
|
||||
if isKeyword(offending) {
|
||||
span = spanFromTokenSafe(offending.Prev().Token(), src)
|
||||
span.Start++
|
||||
span.End++
|
||||
} else {
|
||||
span = spanFromTokenSafe(offending.Token(), src)
|
||||
span.Start++
|
||||
span.End++
|
||||
}
|
||||
|
||||
err.Message = "Unclosed function call"
|
||||
err.Hint = "Add a closing ')' to complete the function call."
|
||||
err.Spans = []ErrorSpan{
|
||||
NewMainErrorSpan(span, "missing ')'"),
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
if isMissing(err.Message) {
|
||||
if isMissingToken(err.Message, ")") {
|
||||
span := spanFromTokenSafe(offending.Token(), src)
|
||||
span.Start++
|
||||
span.End++
|
||||
|
||||
err.Message = "Unclosed parenthesized expression"
|
||||
err.Hint = "Add a closing ')' to complete the expression."
|
||||
err.Spans = []ErrorSpan{
|
||||
NewMainErrorSpan(span, "missing ')'"),
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
if isExtraneous(err.Message) {
|
||||
if is(offending, "(") {
|
||||
span := spanFromTokenSafe(offending.Token(), src)
|
||||
span.Start++
|
||||
span.End++
|
||||
|
||||
err.Message = "Expected a valid list of arguments"
|
||||
err.Hint = "Did you forget to provide a value?"
|
||||
err.Spans = []ErrorSpan{
|
||||
NewMainErrorSpan(span, "missing value"),
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
if is(offending, "..") {
|
||||
span := spanFromTokenSafe(offending.Token(), src)
|
||||
span.Start += 2
|
||||
span.End += 2
|
||||
|
||||
start := offending.Prev().GetText()
|
||||
err.Message = "Expected end value after '..' in range expression"
|
||||
err.Hint = fmt.Sprintf("Provide an end value to complete the range, e.g. %s..10.", start)
|
||||
err.Spans = []ErrorSpan{
|
||||
NewMainErrorSpan(span, "missing value"),
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
@@ -7,68 +7,8 @@ import (
|
||||
"github.com/MontFerret/ferret/pkg/file"
|
||||
)
|
||||
|
||||
func matchCommonErrors(src *file.Source, err *CompilationError, offending *TokenNode) bool {
|
||||
func matchLiteralErrors(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
|
||||
}
|
||||
|
||||
if is(offending.Prev(), "||") || is(offending.Prev(), "OR") ||
|
||||
is(offending.Prev(), "&&") || is(offending.Prev(), "AND") {
|
||||
span := spanFromTokenSafe(offending.Prev().Token(), src)
|
||||
span.Start += 2
|
||||
span.End += 2
|
||||
|
||||
operator := offending.Prev().GetText()
|
||||
err.Message = fmt.Sprintf("Expected right-hand expression after '%s'", operator)
|
||||
err.Hint = fmt.Sprintf("Provide an expression after the logical operator, e.g. (a %s b).", operator)
|
||||
err.Spans = []ErrorSpan{
|
||||
NewMainErrorSpan(span, "missing expression"),
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// Ternary operator, incomplete expression
|
||||
if is(offending.Prev(), "?") {
|
||||
span := spanFromTokenSafe(offending.Prev().Token(), src)
|
||||
span.Start++
|
||||
span.End++
|
||||
|
||||
err.Message = "Expected expression after '?' in ternary operator"
|
||||
err.Hint = "Provide an expression after the question mark to complete the ternary operation."
|
||||
err.Spans = []ErrorSpan{
|
||||
NewMainErrorSpan(span, "missing expression"),
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// Ternary operator, missing the right-hand expression
|
||||
if is(offending.Prev(), ":") {
|
||||
span := spanFromTokenSafe(offending.Prev().Token(), src)
|
||||
span.Start++
|
||||
span.End++
|
||||
|
||||
err.Message = "Expected expression after ':' in ternary operator"
|
||||
err.Hint = "Provide an expression after the colon to complete the ternary operation."
|
||||
err.Spans = []ErrorSpan{
|
||||
NewMainErrorSpan(span, "missing expression"),
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
input := extractNoAlternativeInputs(err.Message)
|
||||
token := input[len(input)-1]
|
||||
|
||||
@@ -115,28 +55,6 @@ func matchCommonErrors(src *file.Source, err *CompilationError, offending *Token
|
||||
}
|
||||
|
||||
if isNoAlternative(err.Message) || isMissing(err.Message) {
|
||||
if is(offending.Prev(), "(") {
|
||||
var span file.Span
|
||||
|
||||
if isKeyword(offending) {
|
||||
span = spanFromTokenSafe(offending.Prev().Token(), src)
|
||||
span.Start++
|
||||
span.End++
|
||||
} else {
|
||||
span = spanFromTokenSafe(offending.Token(), src)
|
||||
span.Start++
|
||||
span.End++
|
||||
}
|
||||
|
||||
err.Message = "Unclosed function call"
|
||||
err.Hint = "Add a closing ')' to complete the function call."
|
||||
err.Spans = []ErrorSpan{
|
||||
NewMainErrorSpan(span, "missing ')'"),
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
if is(offending.Prev(), "[") {
|
||||
var span file.Span
|
||||
|
||||
@@ -225,53 +143,6 @@ func matchCommonErrors(src *file.Source, err *CompilationError, offending *Token
|
||||
}
|
||||
}
|
||||
|
||||
if isMissing(err.Message) {
|
||||
if isMissingToken(err.Message, ")") {
|
||||
span := spanFromTokenSafe(offending.Token(), src)
|
||||
span.Start++
|
||||
span.End++
|
||||
|
||||
err.Message = "Unclosed parenthesized expression"
|
||||
err.Hint = "Add a closing ')' to complete the expression."
|
||||
err.Spans = []ErrorSpan{
|
||||
NewMainErrorSpan(span, "missing ')'"),
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
if isExtraneous(err.Message) {
|
||||
if is(offending, "(") {
|
||||
span := spanFromTokenSafe(offending.Token(), src)
|
||||
span.Start++
|
||||
span.End++
|
||||
|
||||
err.Message = "Expected a valid list of arguments"
|
||||
err.Hint = "Did you forget to provide a value?"
|
||||
err.Spans = []ErrorSpan{
|
||||
NewMainErrorSpan(span, "missing value"),
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
if is(offending, "..") {
|
||||
span := spanFromTokenSafe(offending.Token(), src)
|
||||
span.Start += 2
|
||||
span.End += 2
|
||||
|
||||
start := offending.Prev().GetText()
|
||||
err.Message = "Expected end value after '..' in range expression"
|
||||
err.Hint = fmt.Sprintf("Provide an end value to complete the range, e.g. %s..10.", start)
|
||||
err.Spans = []ErrorSpan{
|
||||
NewMainErrorSpan(span, "missing value"),
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
if isExtraneous(err.Message) || isMismatched(err.Message) {
|
||||
var token string
|
||||
|
@@ -0,0 +1,313 @@
|
||||
package compiler_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/MontFerret/ferret/pkg/compiler"
|
||||
)
|
||||
|
||||
func TestLiteralsSyntaxErrors(t *testing.T) {
|
||||
RunUseCases(t, []UseCase{
|
||||
ErrorCase(
|
||||
`
|
||||
LET i = "foo
|
||||
RETURN i
|
||||
`, E{
|
||||
Kind: compiler.SyntaxError,
|
||||
Message: "Unclosed string literal",
|
||||
Hint: "Add a matching '\"' to close the string.",
|
||||
}, "Incomplete string literal (closing quote missing)"),
|
||||
|
||||
ErrorCase(
|
||||
`
|
||||
LET i = "foo bar
|
||||
RETURN i
|
||||
`, E{
|
||||
Kind: compiler.SyntaxError,
|
||||
Message: "Unclosed string literal",
|
||||
Hint: "Add a matching '\"' to close the string.",
|
||||
}, "Incomplete multi-string literal (closing quote missing)"),
|
||||
|
||||
ErrorCase(
|
||||
`
|
||||
LET i = foo"
|
||||
RETURN i
|
||||
`, E{
|
||||
Kind: compiler.SyntaxError,
|
||||
Message: "Unclosed string literal",
|
||||
Hint: "Add a matching '\"' to close the string.",
|
||||
}, "Incomplete string literal (opening quote missing)"),
|
||||
|
||||
ErrorCase(
|
||||
`
|
||||
LET i = foo bar"
|
||||
RETURN i
|
||||
`, E{
|
||||
Kind: compiler.SyntaxError,
|
||||
Message: "Unclosed string literal",
|
||||
Hint: "Add a matching '\"' to close the string.",
|
||||
}, "Incomplete multi-string literal (opening quote missing)"),
|
||||
|
||||
ErrorCase(
|
||||
`
|
||||
LET i = 'foo
|
||||
RETURN i
|
||||
`, E{
|
||||
Kind: compiler.SyntaxError,
|
||||
Message: "Unclosed string literal",
|
||||
Hint: "Add a matching \"'\" to close the string.",
|
||||
}, "Incomplete string literal (closing quote missing) 2"),
|
||||
|
||||
ErrorCase(
|
||||
`
|
||||
LET i = 'foo bar
|
||||
RETURN i
|
||||
`, E{
|
||||
Kind: compiler.SyntaxError,
|
||||
Message: "Unclosed string literal",
|
||||
Hint: "Add a matching \"'\" to close the string.",
|
||||
}, "Incomplete multi-string literal (closing quote missing) 2"),
|
||||
|
||||
ErrorCase(
|
||||
`
|
||||
LET i = foo'
|
||||
RETURN i
|
||||
`, E{
|
||||
Kind: compiler.SyntaxError,
|
||||
Message: "Unclosed string literal",
|
||||
Hint: "Add a matching \"'\" to close the string.",
|
||||
}, "Incomplete string literal (opening quote missing) 2"),
|
||||
|
||||
ErrorCase(
|
||||
`
|
||||
LET i = foo bar'
|
||||
RETURN i
|
||||
`, E{
|
||||
Kind: compiler.SyntaxError,
|
||||
Message: "Unclosed string literal",
|
||||
Hint: "Add a matching \"'\" to close the string.",
|
||||
}, "Incomplete multi-string literal (opening quote missing) 2"),
|
||||
|
||||
ErrorCase(
|
||||
"LET i = `foo "+
|
||||
"RETURN i", E{
|
||||
Kind: compiler.SyntaxError,
|
||||
Message: "Unclosed string literal",
|
||||
Hint: "Add a matching '`' to close the string.",
|
||||
}, "Incomplete string literal (closing quote missing) 3"),
|
||||
|
||||
ErrorCase(
|
||||
"LET i = `foo bar"+
|
||||
"RETURN i", E{
|
||||
Kind: compiler.SyntaxError,
|
||||
Message: "Unclosed string literal",
|
||||
Hint: "Add a matching '`' to close the string.",
|
||||
}, "Incomplete multi-string literal (closing quote missing) 3"),
|
||||
|
||||
ErrorCase(
|
||||
"LET i = foo` "+
|
||||
"RETURN i", E{
|
||||
Kind: compiler.SyntaxError,
|
||||
Message: "Unclosed string literal",
|
||||
Hint: "Add a matching '`' to close the string.",
|
||||
}, "Incomplete string literal (opening quote missing) 3"),
|
||||
|
||||
ErrorCase(
|
||||
"LET i = foo bar` "+
|
||||
"RETURN i", E{
|
||||
Kind: compiler.SyntaxError,
|
||||
Message: "Unclosed string literal",
|
||||
Hint: "Add a matching '`' to close the string.",
|
||||
}, "Incomplete multi-string literal (opening quote missing) 3"),
|
||||
|
||||
ErrorCase(
|
||||
`
|
||||
LET i = { "foo: }
|
||||
RETURN i
|
||||
`, E{
|
||||
Kind: compiler.SyntaxError,
|
||||
Message: "Unclosed string literal",
|
||||
Hint: "Add a matching '\"' to close the string.",
|
||||
}, "Incomplete string literal (closing quote missing) 4"),
|
||||
|
||||
ErrorCase(
|
||||
`
|
||||
LET i = { "foo bar: }
|
||||
RETURN i
|
||||
`, E{
|
||||
Kind: compiler.SyntaxError,
|
||||
Message: "Unclosed string literal",
|
||||
Hint: "Add a matching '\"' to close the string.",
|
||||
}, "Incomplete multi-string literal (closing quote missing) 4"),
|
||||
|
||||
ErrorCase(
|
||||
`
|
||||
LET i = { foo": }
|
||||
RETURN i
|
||||
`, E{
|
||||
Kind: compiler.SyntaxError,
|
||||
Message: "Unclosed string literal",
|
||||
Hint: "Add a matching '\"' to close the string.",
|
||||
}, "Incomplete string literal (opening quote missing) 4"),
|
||||
|
||||
SkipErrorCase(
|
||||
`
|
||||
LET i = { foo bar": }
|
||||
RETURN i
|
||||
`, E{
|
||||
Kind: compiler.SyntaxError,
|
||||
Message: "Unclosed string literal",
|
||||
Hint: "Add a matching '\"' to close the string",
|
||||
}, "Incomplete multi-string literal (opening quote missing) 4"),
|
||||
|
||||
ErrorCase(
|
||||
`
|
||||
LET i = { 'foo: }
|
||||
RETURN i
|
||||
`, E{
|
||||
Kind: compiler.SyntaxError,
|
||||
Message: "Unclosed string literal",
|
||||
Hint: "Add a matching \"'\" to close the string.",
|
||||
}, "Incomplete string literal (closing quote missing) 5"),
|
||||
|
||||
ErrorCase(
|
||||
`
|
||||
LET i = { foo': }
|
||||
RETURN i
|
||||
`, E{
|
||||
Kind: compiler.SyntaxError,
|
||||
Message: "Unclosed string literal",
|
||||
Hint: "Add a matching \"'\" to close the string.",
|
||||
}, "Incomplete string literal (opening quote missing) 5"),
|
||||
|
||||
SkipErrorCase(
|
||||
`
|
||||
LET i = { foo bar': }
|
||||
RETURN i
|
||||
`, E{
|
||||
Kind: compiler.SyntaxError,
|
||||
Message: "Unclosed string literal",
|
||||
Hint: "Add a matching \"'\" to close the string.",
|
||||
}, "Incomplete multi-string literal (opening quote missing) 5"),
|
||||
|
||||
ErrorCase(
|
||||
"LET i = { 'foo: }"+
|
||||
"RETURN i", E{
|
||||
Kind: compiler.SyntaxError,
|
||||
Message: "Unclosed string literal",
|
||||
Hint: "Add a matching \"'\" to close the string.",
|
||||
}, "Incomplete string literal (closing quote missing) 6"),
|
||||
|
||||
ErrorCase(
|
||||
"LET i = { 'foo bar: }"+
|
||||
"RETURN i", E{
|
||||
Kind: compiler.SyntaxError,
|
||||
Message: "Unclosed string literal",
|
||||
Hint: "Add a matching \"'\" to close the string.",
|
||||
}, "Incomplete multi-string literal (closing quote missing) 6"),
|
||||
|
||||
SkipErrorCase(
|
||||
`
|
||||
LET o = { foo: "bar" }
|
||||
LET i = o.
|
||||
RETURN i
|
||||
`, E{
|
||||
Kind: compiler.SyntaxError,
|
||||
Message: "Expected expression after '=' for variable 'i'",
|
||||
Hint: "Did you forget to provide a value?",
|
||||
}, "Incomplete member access"),
|
||||
|
||||
SkipErrorCase(
|
||||
`
|
||||
LET o = { foo: "bar" }
|
||||
LET i = o.
|
||||
FUNC(i)
|
||||
RETURN i
|
||||
`, E{
|
||||
Kind: compiler.SyntaxError,
|
||||
Message: "Expected expression after '=' for variable 'i'",
|
||||
Hint: "Did you forget to provide a value?",
|
||||
}, "Incomplete member access 2"),
|
||||
|
||||
ErrorCase(
|
||||
`
|
||||
LET i = [
|
||||
RETURN i
|
||||
`, E{
|
||||
Kind: compiler.SyntaxError,
|
||||
Message: "Unclosed array literal",
|
||||
Hint: "Add a closing ']' to complete the array.",
|
||||
}, "Incomplete array literal"),
|
||||
|
||||
ErrorCase(
|
||||
`
|
||||
LET i = [1
|
||||
RETURN i
|
||||
`, E{
|
||||
Kind: compiler.SyntaxError,
|
||||
Message: "Unclosed array literal",
|
||||
Hint: "Add a closing ']' to complete the array.",
|
||||
}, "Incomplete array literal 2"),
|
||||
|
||||
ErrorCase(
|
||||
`
|
||||
LET i = [,]
|
||||
RETURN i
|
||||
`, E{
|
||||
Kind: compiler.SyntaxError,
|
||||
Message: "Expected a valid list of values",
|
||||
Hint: "Did you forget to provide a value?",
|
||||
}, "Incomplete array literal 3"),
|
||||
|
||||
ErrorCase(
|
||||
`
|
||||
LET i = {
|
||||
RETURN i
|
||||
`, E{
|
||||
Kind: compiler.SyntaxError,
|
||||
Message: "Unclosed object literal",
|
||||
Hint: "Add a closing '}' to complete the object.",
|
||||
}, "Incomplete object literal"),
|
||||
|
||||
ErrorCase(
|
||||
`
|
||||
LET i = { foo: }
|
||||
RETURN i
|
||||
`, E{
|
||||
Kind: compiler.SyntaxError,
|
||||
Message: "Expected value after object property name",
|
||||
Hint: "Provide a value for the property, e.g. { foo: 123 }.",
|
||||
}, "Incomplete object literal 2"),
|
||||
|
||||
ErrorCase(
|
||||
`
|
||||
LET i = { : }
|
||||
RETURN i
|
||||
`, E{
|
||||
Kind: compiler.SyntaxError,
|
||||
Message: "Expected property name before ':'",
|
||||
Hint: "Object properties must have a name before the colon, e.g. { property: 123 }.",
|
||||
}, "Incomplete object literal 3"),
|
||||
|
||||
SkipErrorCase(
|
||||
`
|
||||
LET i = { a 123 }
|
||||
RETURN i
|
||||
`, E{
|
||||
Kind: compiler.SyntaxError,
|
||||
Message: "Expected property name before ':'",
|
||||
Hint: "Object properties must have a name before the colon, e.g. { property: 123 }.",
|
||||
}, "Incomplete object literal 4"),
|
||||
|
||||
ErrorCase(
|
||||
`
|
||||
LET arr = [1, 2, 3]
|
||||
LET v = arr[1
|
||||
RETURN v
|
||||
`, E{
|
||||
Kind: compiler.SyntaxError,
|
||||
Message: "Expected end value before '..' in range expression",
|
||||
Hint: "Object properties must have a name before the colon, e.g. { property: 123 }.",
|
||||
}, "Incomplete array access"),
|
||||
})
|
||||
}
|
@@ -37,204 +37,6 @@ func TestSyntaxErrors(t *testing.T) {
|
||||
Hint: "Did you forget to provide a variable name?",
|
||||
}, "Missing variable name 3"),
|
||||
|
||||
ErrorCase(
|
||||
`
|
||||
LET i = "foo
|
||||
RETURN i
|
||||
`, E{
|
||||
Kind: compiler.SyntaxError,
|
||||
Message: "Unclosed string literal",
|
||||
Hint: "Add a matching '\"' to close the string.",
|
||||
}, "Incomplete string (closing quote missing)"),
|
||||
|
||||
ErrorCase(
|
||||
`
|
||||
LET i = "foo bar
|
||||
RETURN i
|
||||
`, E{
|
||||
Kind: compiler.SyntaxError,
|
||||
Message: "Unclosed string literal",
|
||||
Hint: "Add a matching '\"' to close the string.",
|
||||
}, "Incomplete multi-string (closing quote missing)"),
|
||||
|
||||
ErrorCase(
|
||||
`
|
||||
LET i = foo"
|
||||
RETURN i
|
||||
`, E{
|
||||
Kind: compiler.SyntaxError,
|
||||
Message: "Unclosed string literal",
|
||||
Hint: "Add a matching '\"' to close the string.",
|
||||
}, "Incomplete string (opening quote missing)"),
|
||||
|
||||
ErrorCase(
|
||||
`
|
||||
LET i = foo bar"
|
||||
RETURN i
|
||||
`, E{
|
||||
Kind: compiler.SyntaxError,
|
||||
Message: "Unclosed string literal",
|
||||
Hint: "Add a matching '\"' to close the string.",
|
||||
}, "Incomplete multi-string (opening quote missing)"),
|
||||
|
||||
ErrorCase(
|
||||
`
|
||||
LET i = 'foo
|
||||
RETURN i
|
||||
`, E{
|
||||
Kind: compiler.SyntaxError,
|
||||
Message: "Unclosed string literal",
|
||||
Hint: "Add a matching \"'\" to close the string.",
|
||||
}, "Incomplete string (closing quote missing) 2"),
|
||||
|
||||
ErrorCase(
|
||||
`
|
||||
LET i = 'foo bar
|
||||
RETURN i
|
||||
`, E{
|
||||
Kind: compiler.SyntaxError,
|
||||
Message: "Unclosed string literal",
|
||||
Hint: "Add a matching \"'\" to close the string.",
|
||||
}, "Incomplete multi-string (closing quote missing) 2"),
|
||||
|
||||
ErrorCase(
|
||||
`
|
||||
LET i = foo'
|
||||
RETURN i
|
||||
`, E{
|
||||
Kind: compiler.SyntaxError,
|
||||
Message: "Unclosed string literal",
|
||||
Hint: "Add a matching \"'\" to close the string.",
|
||||
}, "Incomplete string (opening quote missing) 2"),
|
||||
|
||||
ErrorCase(
|
||||
`
|
||||
LET i = foo bar'
|
||||
RETURN i
|
||||
`, E{
|
||||
Kind: compiler.SyntaxError,
|
||||
Message: "Unclosed string literal",
|
||||
Hint: "Add a matching \"'\" to close the string.",
|
||||
}, "Incomplete multi-string (opening quote missing) 2"),
|
||||
|
||||
ErrorCase(
|
||||
"LET i = `foo "+
|
||||
"RETURN i", E{
|
||||
Kind: compiler.SyntaxError,
|
||||
Message: "Unclosed string literal",
|
||||
Hint: "Add a matching '`' to close the string.",
|
||||
}, "Incomplete string (closing quote missing) 3"),
|
||||
|
||||
ErrorCase(
|
||||
"LET i = `foo bar"+
|
||||
"RETURN i", E{
|
||||
Kind: compiler.SyntaxError,
|
||||
Message: "Unclosed string literal",
|
||||
Hint: "Add a matching '`' to close the string.",
|
||||
}, "Incomplete multi-string (closing quote missing) 3"),
|
||||
|
||||
ErrorCase(
|
||||
"LET i = foo` "+
|
||||
"RETURN i", E{
|
||||
Kind: compiler.SyntaxError,
|
||||
Message: "Unclosed string literal",
|
||||
Hint: "Add a matching '`' to close the string.",
|
||||
}, "Incomplete string (opening quote missing) 3"),
|
||||
|
||||
ErrorCase(
|
||||
"LET i = foo bar` "+
|
||||
"RETURN i", E{
|
||||
Kind: compiler.SyntaxError,
|
||||
Message: "Unclosed string literal",
|
||||
Hint: "Add a matching '`' to close the string.",
|
||||
}, "Incomplete multi-string (opening quote missing) 3"),
|
||||
|
||||
ErrorCase(
|
||||
`
|
||||
LET i = { "foo: }
|
||||
RETURN i
|
||||
`, E{
|
||||
Kind: compiler.SyntaxError,
|
||||
Message: "Unclosed string literal",
|
||||
Hint: "Add a matching '\"' to close the string.",
|
||||
}, "Incomplete string (closing quote missing) 4"),
|
||||
|
||||
ErrorCase(
|
||||
`
|
||||
LET i = { "foo bar: }
|
||||
RETURN i
|
||||
`, E{
|
||||
Kind: compiler.SyntaxError,
|
||||
Message: "Unclosed string literal",
|
||||
Hint: "Add a matching '\"' to close the string.",
|
||||
}, "Incomplete multi-string (closing quote missing) 4"),
|
||||
|
||||
ErrorCase(
|
||||
`
|
||||
LET i = { foo": }
|
||||
RETURN i
|
||||
`, E{
|
||||
Kind: compiler.SyntaxError,
|
||||
Message: "Unclosed string literal",
|
||||
Hint: "Add a matching '\"' to close the string.",
|
||||
}, "Incomplete string (opening quote missing) 4"),
|
||||
|
||||
SkipErrorCase(
|
||||
`
|
||||
LET i = { foo bar": }
|
||||
RETURN i
|
||||
`, E{
|
||||
Kind: compiler.SyntaxError,
|
||||
Message: "Unclosed string literal",
|
||||
Hint: "Add a matching '\"' to close the string",
|
||||
}, "Incomplete multi-string (opening quote missing) 4"),
|
||||
|
||||
ErrorCase(
|
||||
`
|
||||
LET i = { 'foo: }
|
||||
RETURN i
|
||||
`, E{
|
||||
Kind: compiler.SyntaxError,
|
||||
Message: "Unclosed string literal",
|
||||
Hint: "Add a matching \"'\" to close the string.",
|
||||
}, "Incomplete string (closing quote missing) 5"),
|
||||
|
||||
ErrorCase(
|
||||
`
|
||||
LET i = { foo': }
|
||||
RETURN i
|
||||
`, E{
|
||||
Kind: compiler.SyntaxError,
|
||||
Message: "Unclosed string literal",
|
||||
Hint: "Add a matching \"'\" to close the string.",
|
||||
}, "Incomplete string (opening quote missing) 5"),
|
||||
|
||||
SkipErrorCase(
|
||||
`
|
||||
LET i = { foo bar': }
|
||||
RETURN i
|
||||
`, E{
|
||||
Kind: compiler.SyntaxError,
|
||||
Message: "Unclosed string literal",
|
||||
Hint: "Add a matching \"'\" to close the string.",
|
||||
}, "Incomplete multi-string (opening quote missing) 5"),
|
||||
|
||||
ErrorCase(
|
||||
"LET i = { 'foo: }"+
|
||||
"RETURN i", E{
|
||||
Kind: compiler.SyntaxError,
|
||||
Message: "Unclosed string literal",
|
||||
Hint: "Add a matching \"'\" to close the string.",
|
||||
}, "Incomplete string (closing quote missing) 6"),
|
||||
|
||||
ErrorCase(
|
||||
"LET i = { 'foo bar: }"+
|
||||
"RETURN i", E{
|
||||
Kind: compiler.SyntaxError,
|
||||
Message: "Unclosed string literal",
|
||||
Hint: "Add a matching \"'\" to close the string.",
|
||||
}, "Incomplete multi-string (closing quote missing) 6"),
|
||||
|
||||
ErrorCase(
|
||||
`
|
||||
LET i = NONE
|
||||
@@ -391,29 +193,6 @@ func TestSyntaxErrors(t *testing.T) {
|
||||
Hint: "Did you forget to provide a value?",
|
||||
}, "Missing variable assignment value 3"),
|
||||
|
||||
SkipErrorCase(
|
||||
`
|
||||
LET o = { foo: "bar" }
|
||||
LET i = o.
|
||||
RETURN i
|
||||
`, E{
|
||||
Kind: compiler.SyntaxError,
|
||||
Message: "Expected expression after '=' for variable 'i'",
|
||||
Hint: "Did you forget to provide a value?",
|
||||
}, "Incomplete member access"),
|
||||
|
||||
SkipErrorCase(
|
||||
`
|
||||
LET o = { foo: "bar" }
|
||||
LET i = o.
|
||||
FUNC(i)
|
||||
RETURN i
|
||||
`, E{
|
||||
Kind: compiler.SyntaxError,
|
||||
Message: "Expected expression after '=' for variable 'i'",
|
||||
Hint: "Did you forget to provide a value?",
|
||||
}, "Incomplete member access 2"),
|
||||
|
||||
ErrorCase(
|
||||
`
|
||||
FUNC(1,
|
||||
@@ -454,76 +233,6 @@ func TestSyntaxErrors(t *testing.T) {
|
||||
Hint: "Add a closing ')' to complete the function call.",
|
||||
}, "Incomplete function call 4"),
|
||||
|
||||
ErrorCase(
|
||||
`
|
||||
LET i = [
|
||||
RETURN i
|
||||
`, E{
|
||||
Kind: compiler.SyntaxError,
|
||||
Message: "Unclosed array literal",
|
||||
Hint: "Add a closing ']' to complete the array.",
|
||||
}, "Incomplete array literal"),
|
||||
|
||||
ErrorCase(
|
||||
`
|
||||
LET i = [1
|
||||
RETURN i
|
||||
`, E{
|
||||
Kind: compiler.SyntaxError,
|
||||
Message: "Unclosed array literal",
|
||||
Hint: "Add a closing ']' to complete the array.",
|
||||
}, "Incomplete array literal 2"),
|
||||
|
||||
ErrorCase(
|
||||
`
|
||||
LET i = [,]
|
||||
RETURN i
|
||||
`, E{
|
||||
Kind: compiler.SyntaxError,
|
||||
Message: "Expected a valid list of values",
|
||||
Hint: "Did you forget to provide a value?",
|
||||
}, "Incomplete array literal 3"),
|
||||
|
||||
ErrorCase(
|
||||
`
|
||||
LET i = {
|
||||
RETURN i
|
||||
`, E{
|
||||
Kind: compiler.SyntaxError,
|
||||
Message: "Unclosed object literal",
|
||||
Hint: "Add a closing '}' to complete the object.",
|
||||
}, "Incomplete object literal"),
|
||||
|
||||
ErrorCase(
|
||||
`
|
||||
LET i = { foo: }
|
||||
RETURN i
|
||||
`, E{
|
||||
Kind: compiler.SyntaxError,
|
||||
Message: "Expected value after object property name",
|
||||
Hint: "Provide a value for the property, e.g. { foo: 123 }.",
|
||||
}, "Incomplete object literal 2"),
|
||||
|
||||
ErrorCase(
|
||||
`
|
||||
LET i = { : }
|
||||
RETURN i
|
||||
`, E{
|
||||
Kind: compiler.SyntaxError,
|
||||
Message: "Expected property name before ':'",
|
||||
Hint: "Object properties must have a name before the colon, e.g. { property: 123 }.",
|
||||
}, "Incomplete object literal 3"),
|
||||
|
||||
SkipErrorCase(
|
||||
`
|
||||
LET i = { a 123 }
|
||||
RETURN i
|
||||
`, E{
|
||||
Kind: compiler.SyntaxError,
|
||||
Message: "Expected property name before ':'",
|
||||
Hint: "Object properties must have a name before the colon, e.g. { property: 123 }.",
|
||||
}, "Incomplete object literal 4"),
|
||||
|
||||
ErrorCase(
|
||||
`
|
||||
LET r = 0..
|
||||
|
Reference in New Issue
Block a user