From b811f17f6a73aa3af7d7761e31720e17f05d68d2 Mon Sep 17 00:00:00 2001 From: Tim Voronov Date: Tue, 12 Aug 2025 13:38:15 -0400 Subject: [PATCH] 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. --- .../internal/diagnostics/error_analyzer.go | 1 + ...nt_errors.go => match_error_assignment.go} | 0 .../diagnostics/match_error_common.go | 144 ++++++++ ...loop_errors.go => match_error_for_loop.go} | 0 ...mmon_errors.go => match_error_literals.go} | 131 +------- ..._value_errors.go => match_error_return.go} | 0 .../compiler_errors_syntax_literals_test.go | 313 ++++++++++++++++++ .../compiler/compiler_errors_syntax_test.go | 291 ---------------- 8 files changed, 459 insertions(+), 421 deletions(-) rename pkg/compiler/internal/diagnostics/{match_assignment_errors.go => match_error_assignment.go} (100%) create mode 100644 pkg/compiler/internal/diagnostics/match_error_common.go rename pkg/compiler/internal/diagnostics/{match_for_loop_errors.go => match_error_for_loop.go} (100%) rename pkg/compiler/internal/diagnostics/{match_common_errors.go => match_error_literals.go} (57%) rename pkg/compiler/internal/diagnostics/{match_return_value_errors.go => match_error_return.go} (100%) create mode 100644 test/integration/compiler/compiler_errors_syntax_literals_test.go diff --git a/pkg/compiler/internal/diagnostics/error_analyzer.go b/pkg/compiler/internal/diagnostics/error_analyzer.go index 4715164a..95b0c7fe 100644 --- a/pkg/compiler/internal/diagnostics/error_analyzer.go +++ b/pkg/compiler/internal/diagnostics/error_analyzer.go @@ -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, diff --git a/pkg/compiler/internal/diagnostics/match_assignment_errors.go b/pkg/compiler/internal/diagnostics/match_error_assignment.go similarity index 100% rename from pkg/compiler/internal/diagnostics/match_assignment_errors.go rename to pkg/compiler/internal/diagnostics/match_error_assignment.go diff --git a/pkg/compiler/internal/diagnostics/match_error_common.go b/pkg/compiler/internal/diagnostics/match_error_common.go new file mode 100644 index 00000000..695daf26 --- /dev/null +++ b/pkg/compiler/internal/diagnostics/match_error_common.go @@ -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 +} diff --git a/pkg/compiler/internal/diagnostics/match_for_loop_errors.go b/pkg/compiler/internal/diagnostics/match_error_for_loop.go similarity index 100% rename from pkg/compiler/internal/diagnostics/match_for_loop_errors.go rename to pkg/compiler/internal/diagnostics/match_error_for_loop.go diff --git a/pkg/compiler/internal/diagnostics/match_common_errors.go b/pkg/compiler/internal/diagnostics/match_error_literals.go similarity index 57% rename from pkg/compiler/internal/diagnostics/match_common_errors.go rename to pkg/compiler/internal/diagnostics/match_error_literals.go index 00d764d0..b6726173 100644 --- a/pkg/compiler/internal/diagnostics/match_common_errors.go +++ b/pkg/compiler/internal/diagnostics/match_error_literals.go @@ -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 diff --git a/pkg/compiler/internal/diagnostics/match_return_value_errors.go b/pkg/compiler/internal/diagnostics/match_error_return.go similarity index 100% rename from pkg/compiler/internal/diagnostics/match_return_value_errors.go rename to pkg/compiler/internal/diagnostics/match_error_return.go diff --git a/test/integration/compiler/compiler_errors_syntax_literals_test.go b/test/integration/compiler/compiler_errors_syntax_literals_test.go new file mode 100644 index 00000000..ba0a372f --- /dev/null +++ b/test/integration/compiler/compiler_errors_syntax_literals_test.go @@ -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"), + }) +} diff --git a/test/integration/compiler/compiler_errors_syntax_test.go b/test/integration/compiler/compiler_errors_syntax_test.go index b490331f..7781efac 100644 --- a/test/integration/compiler/compiler_errors_syntax_test.go +++ b/test/integration/compiler/compiler_errors_syntax_test.go @@ -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..