1
0
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:
Tim Voronov
2025-08-12 13:38:15 -04:00
parent 3cd9c9a434
commit b811f17f6a
8 changed files with 459 additions and 421 deletions

View File

@@ -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,

View 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
}

View File

@@ -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

View File

@@ -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"),
})
}

View File

@@ -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..